B. Complete The Graph

发布时间 2023-05-25 15:04:19作者: onlyblues

B. Complete The Graph

ZS the Coder has drawn an undirected graph of $n$ vertices numbered from $0$ to $n - 1$ and $m$ edges between them. Each edge of the graph is weighted, each weight is a positive integer.

The next day, ZS the Coder realized that some of the weights were erased! So he wants to reassign positive integer weight to each of the edges which weights were erased, so that the length of the shortest path between vertices $s$ and $t$ in the resulting graph is exactly $L$. Can you help him?

Input

The first line contains five integers $n$, $m$, $L$, $s$, $t$ $(2 \leq n \leq 1000,  1 \leq m \leq 10000,  1 \leq L \leq 10^9,  0 \leq s, t \leq n - 1,  s \ne t)$ — the number of vertices, number of edges, the desired length of shortest path, starting vertex and ending vertex respectively.

Then, $m$ lines describing the edges of the graph follow. $i$-th of them contains three integers, $u_i$, $v_i$, $w_i$ $(0 \leq u_i, v_i \leq n - 1,  u_i \ne v_i,  0 \leq w_i \leq 10^9)$. $u_i$ and $v_i$ denote the endpoints of the edge and $w_i$ denotes its weight. If $w_i$ is equal to $0$ then the weight of the corresponding edge was erased.

It is guaranteed that there is at most one edge between any pair of vertices.

Output

Print "NO" (without quotes) in the only line if it's not possible to assign the weights in a required way.

Otherwise, print "YES" in the first line. Next *m* lines should contain the edges of the resulting graph, with weights assigned to edges which weights were erased. $i$-th of them should contain three integers $u_i$, $v_i$ and $w_i$, denoting an edge between vertices $u_i$ and $v_i$ of weight $w_i$. The edges of the new graph must coincide with the ones in the graph from the input. The weights that were not erased must remain unchanged whereas the new weights can be any positive integer not exceeding ${10}^{18}$.

The order of the edges in the output doesn't matter. The length of the shortest path between $s$ and $t$ must be equal to $L$.

If there are multiple solutions, print any of them.

Examples

input

5 5 13 0 4
0 1 5
2 1 2
3 2 3
1 4 0
4 3 4

output

YES
0 1 5
2 1 2
3 2 3
1 4 8
4 3 4

input

2 1 123456789 0 1
0 1 0

output

YES
0 1 123456789

input

2 1 999999999 1 0
0 1 1000000000

output

NO

Note

Here's how the graph in the first sample case looks like :

In the first sample case, there is only one missing edge weight. Placing the weight of $8$ gives a shortest path from $0$ to $4$ of length $13$.

In the second sample case, there is only a single edge. Clearly, the only way is to replace the missing weight with $123456789$.

In the last sample case, there is no weights to assign but the length of the shortest path doesn't match the required value, so the answer is "NO".

 

解题思路

  leetcode的周赛的题目Modify Graph Edge Weights,直接搬了cf上的这道题,这里就写一下这道题的题解吧。

  定义边权为$0$的边为变量边。首先,我们先忽略所有的变量边,对这个图跑一边从$s$到$t$的最短路,得到最短距离$d_t$。如果$d_t < L$那么无解,因为无论我们给变量边赋什么值(正整数),从$s$到$t$的最短路都不会超过$d_t$。

  再考虑把所有的变量边赋值为$1$,然后再跑一遍从$s$到$t$的最短路。此时如果$d_t > L$那么无解,因为每条变量边赋值最小为$1$,如果增大变量边的值那么$d_t$只会变大,故从$s$到$t$的最短路永远不会是$L$。

  此时我们就排除了两种无解的情况,实际上只要排除了这两种情况那么一定有解。当有解时,结合第一种情况,此时有结论从$s$到$t$不经过变量边的最短路一定大于等于$L$。而结合第二种情况,此时一定存在从$s$到$t$距离小于等于$L$的路径,并且根据结论知道这些路径一定是包含变量边的,因此从$s$到$t$的最短路一定包含变量边。因此我们可以修改最短路中变量边的值,使得从$s$到$t$的路径长度恰好为$L$,同时由于我们只关心从$s$到$t$的最短路中的变量边,因此可以将不在最短路中的变量边均赋值为正无穷(${10}^9+1$即可)。不过需要注意的是虽然使得这条最短路的长度变成了$L$,但可能还会存在其他从$s$到$t$距离小于$L$的路径。由于从$s$到$t$长度小于$L$的路径一定包含变量边,这时我们只需要重复上面的步骤去修改最短路中的变量边,直到最短路恰好为$L$(即不存在从$s$到$t$距离小于$L$的路径)。这就证明了一定有解。

  算法流程就是上面的做法,我们需要找到一条当前从$s$到$t$的最短路径,然后再去修改最短路径上的变量边。同时由于每条边的权值为正整数,因此从$s$到$t$最多会经过$n-1$条边,所以我们最多修改$n-1$条经过$s$到$t$路径上的变量边就可以使得最短路恰好为$L$。这里跑最短路用的是$\text{Dijkstra}$算法,在跑最短路时用一个$\text{path}$数组记录每个点是从哪个点转移来的,这样就可以得到从$s$到$t$的最短路径所经过的所有点。然后还需要存每条边与编号的映射,具体来说有无向边$(u, v)$,由于节点编号为$0 \sim n-1$,因此可以把边映射为单个整数$u \cdot n + v$与$v \cdot n + u$。然后根据$\text{path}$数组与映射关系来枚举从$s$到$t$的最短路径所经过的边,选择任意一条变量边(不失一般性这里选择第一条枚举到的变量边),将其赋值为$L - d_t + 1$,即可将当前的最短路径长度变成$L$。同时把不在这条最短路上的变量边赋值为正无穷。最后再跑一次最短路,看看是否满足$d_t = L$,满足则输出答案,否则继续重复上面的步骤。

  AC代码如下,时间复杂度为$O(n(n+m) \log{m})$:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 typedef pair<LL, int> PII;
 6 
 7 const int N = 1010, M = 2e4 + 10;
 8 
 9 int n, m, l, s, t;
10 int head[N], e[M], wts[M], ne[M], idx;
11 LL dist[N];
12 bool vis[N];
13 int path[N];    // 记录最短每个点从哪个点转移过来
14 int mp[N * N];  // 存边与编号的映射
15 unordered_set<int> st;  // 存变量边的编号
16 
17 void add(int v, int w, int wt) {
18     e[idx] = w, wts[idx] = wt, ne[idx] = head[v], head[v] = idx++;
19 }
20 
21 void dijkstra() {
22     memset(dist, 0x3f, sizeof(dist));
23     dist[s] = 0;
24     priority_queue<PII, vector<PII>, greater<PII>> pq;
25     pq.push({0, s});
26     memset(vis, 0, sizeof(vis));
27     memset(path, -1, sizeof(path));
28     while (!pq.empty()) {
29         int t = pq.top().second;
30         pq.pop();
31         if (vis[t]) continue;
32         vis[t] = true;
33         for (int i = head[t]; i != -1; i = ne[i]) {
34             if (dist[e[i]] > dist[t] + wts[i]) {
35                 dist[e[i]] = dist[t] + wts[i];
36                 path[e[i]] = t;
37                 pq.push({dist[e[i]], e[i]});
38             }
39         }
40     }
41 }
42 
43 int main() {
44     scanf("%d %d %d %d %d", &n, &m, &l, &s, &t);
45     memset(head, -1, sizeof(head));
46     for (int i = 0; i < m; i++) {
47         int v, w, wt;
48         scanf("%d %d %d", &v, &w, &wt);
49         mp[v * n + w] = idx, mp[w * n + v] = idx + 1;   // 把无向边(v,w)映射到v*n+w和w*n+v
50         if (!wt) st.insert(idx), st.insert(idx + 1);    // 记录变量边
51         add(v, w, wt), add(w, v, wt);
52     }
53     for (auto &i : st) {    // 第一次跑最短路需要忽略变量边,赋值为正无穷即可
54         wts[i] = 1e9 + 1;
55     }
56     dijkstra();
57     if (dist[t] < l) {  // 从s到t的最短路<L,无解
58         printf("NO");
59         return 0;
60     }
61     for (auto &i : st) {    // 再把所有的变量边赋值为1跑最短路
62         wts[i] = 1;
63     }
64     dijkstra();
65     if (dist[t] > l) {  // 从s到t的最短路>L,无解
66         printf("NO\n");
67         return 0;
68     }
69     else if (dist[t] < l) { // 有解
70         while (dist[t] < l) {   // 只要从s到t的最短路小于L则重复过程,最多执行n-1次
71             for (auto &i : st) {    // 先把所有变量边赋值为正无穷,之后对从s到t最短路上的变量边进行赋值
72                 wts[i] = 1e9 + 1;
73             }
74             int p = t;
75             bool flag = true;   // 用来记录是否枚举到第一条变量边
76             while (path[p] != -1) {
77                 int i = mp[p * n + path[p]], j = mp[path[p] * n + p];   // 得到这条边的编号
78                 if (st.count(i)) {  // 这条边是变量边
79                     if (flag) wts[i] = wts[j] = l - dist[t] + 1, flag = false;  // 枚举到第一条变量边,赋值为L-dt+1
80                     else wts[i] = wts[j] = 1;   // 其他的变量边赋值为1
81                 }
82                 p = path[p];
83             }
84             dijkstra(); // 再跑一次最短路
85         }
86     }
87     printf("YES\n");
88     for (int i = 0; i < n; i++) {
89         for (int j = head[i]; j != -1; j = ne[j]) {
90             if (i < e[j]) printf("%d %d %d\n", i, e[j], wts[j]);
91         }
92     }
93     
94     return 0;
95 }

 

参考资料

  Codeforces Round #372 Editorial:https://codeforces.com/blog/entry/47169

  第 346 场力扣周赛:https://leetcode.cn/circle/discuss/fwWHZg/view/3muHjG/