CodeForces1486E Paired Payment {图论}【最短路】(Dijkstra)

题意

    给一张边数为 m m m,点数为 n n n的无向图(并不一定连通)。题目给了一种特定的走法。

  • 每次只能走两条边。现在命这两条边的权值分别为 d i s a dis_a disa​和 d i s b dis_b disb​。我们经过这两天边需要的权值为 ( d i s a + d i s b ) ∗ ( d i s a + d i s b ) (dis_a + dis_b) * (dis_a + dis_b) (disa​+disb​)∗(disa​+disb​)

    问从1出发到达每个点需要的最小权值分别为多少。

题解

    注意题目中,每条边的权值给定的范围是 [ 1 , 50 ] [1,50] [1,50]。因此我们可以在 D i j k s t r a Dijkstra Dijkstra算法的标记数组 v i s [ N ] vis[N] vis[N]的基础上,将其改进为 v i s [ 51 ] [ N ] vis[51][N] vis[51][N],通式为 v i s [ d i s ] [ u ] vis[dis][u] vis[dis][u]。即记录是否通过权值为 d i s dis dis的边到达点 u u u。 d i s [ 51 ] [ N ] dis[51][N] dis[51][N]同理,即表示通过权值为 d i s dis dis的边到达点 u u u花费的权值是多少。
    由于题目一次要连续走两步,而该题数据量有 1 e 5 1e5 1e5,显然通过二重循环达到一次走两步的方法的复杂度是不可行的。这时候就需要巧妙地运用上面改进的两个变量了。
    当我们走了奇数条边的时候,显然这个时候转移才进行到一半,我们需要将走过的边的权值和总权值都记录下来,以便下一次处理。
    当我们走了偶数条边的时候,说明已经到达了目标点,那我们就通过之前记录的信息计算出到达当前点花费的权值。
    具体内容参照代码。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#define lowbit(x) ((x) & -(x))
#define endl "\n"
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10, NN = 5e1 + 10, INF = 0x3f3f3f3f, LEN = 20;
const ll MOD = 1e9 + 7;
const ull seed = 31;
const double pi = acos(-1.0), eps = 1e-8;
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
struct Edge {
    int next, to, dis;
} edge[N];
struct Node {
    int id, dis, pre;
    Node(int id, int dis, int pre) : id(id), dis(dis), pre(pre) {}
    bool friend operator<(const Node& a, const Node& b) {
        return a.dis > b.dis;
    }
};
int n, m, num_edge;
int head[N];
int dis[NN][N];
bool vis[NN][N];
void add_edge(int from, int to, int dis) {
    edge[num_edge].next = head[from];
    edge[num_edge].to = to;
    edge[num_edge].dis = dis;
    head[from] = num_edge++;
}
void dijkstra() {
    priority_queue<Node> q;
    memset(dis, INF, sizeof dis);
    dis[0][1] = 0;
    q.push(Node(1, 0, 0));
    while (!q.empty()) {
        Node u = q.top();
        q.pop();
        if (vis[u.pre][u.id])
            continue;
        vis[u.pre][u.id] = true;
        for (int i = head[u.id]; i != -1; i = edge[i].next) {
            Edge v = edge[i];
            if (u.pre == 0) {
                if (u.dis + v.dis < dis[v.dis][v.to]) { // 此时走过奇数条边
                    dis[v.dis][v.to] = u.dis + v.dis;
                    q.push(Node(v.to, u.dis + v.dis, v.dis));
                }
            } else {
                // 此时走过偶数条边
                ll Dist = u.dis - u.pre + (v.dis + u.pre) * (v.dis + u.pre);
                // u.dis要减去u.pre的原因是在73行,为了方便,暂时将u.pre累加进了总权值
                // 而其实u.pre是要通过和v.dis的计算才能正式进入总权值的
                if (Dist < dis[0][v.to]) {
                    dis[0][v.to] = Dist;
                    q.push(Node(v.to, Dist, 0));
                }
            }
        }
    }
    for (int i = 1; i <= n; ++i) {
        if (dis[0][i] == INF)
            printf("-1 ");
        else
            printf("%d ", dis[0][i]);
    }
}
void init() {
    num_edge = 0;
    memset(head, -1, sizeof head);
}
int main() {
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    // cout.tie(0);
    // freopen("input.txt", "r", stdin);
    // freopen("output.txt", "w", stdout);
    n = read();
    m = read();
    init();
    for (int i = 1; i <= m; ++i) {
        int u, v, w;
        u = read();
        v = read();
        w = read();
        add_edge(u, v, w);
        add_edge(v, u, w);
    }
    dijkstra();
    return 0;
}
上一篇:Docker入门-使用registry镜像创建私有仓库


下一篇:最新Excel个税计算公式来了,说说你每月该交多少税