ABC373D
题意
给定 \(n\) 个点,\(m\) 条有向边,每条有向边从 \(u_i\) 指向 \(v_i\),边权为 \(w_i\)。构造一个长度为 \(n\) 的整数序列 \(x\),满足:
-
\(-10^{18} \le x_i \le 10^{18},1 \le i \le n\)。
-
\(x_{v_j} - x_{u_j} = w_j,1 \le j \le m\)。
数据保证有解。
解法
注意到数据保证有解,因此一定有多组的解(在某组解上同时加或减一个数,差显然不变),构造起来也比较容易。直接遍历全图,对于当前的第 \(j\) 条边,若 \(v_j\) 未遍历,则令 $ x_{v_j} \leftarrow x_{u_j} + w_j$ 即可,并遍历 \(v_j\)。这样做保证了时间复杂度为 \(O(n+m)\)。
但是题目给的是有向图,如果随意选择某个点进入则不一定能把整个连通块遍历完。考虑对等式两遍同乘 \(-1\),则有 \(x_{u_j} - x_{v_j} = -w_j\),那么我们再建一条从 \(v_j\) 到 \(u_j\),边权为 \(-w_j\) 的边,即可确保遍历到同一连通块的每个点。
实现时要判断每个点是否被遍历(题目没有保证给定的是连通图)。
#include <iostream>
#include <cstdio>
#include <cmath>
#define int long long
using namespace std;
struct E
{
int v,w,nxt;
}e[1000001];
int n,m,vis[1000001],hd[1000001],ans[1000001],tot;
void dfs( int u )
{
vis[u] = 1;
int v;
for( int i = hd[u] ; i ; i = e[i].nxt )
{
v = e[i].v;
if( vis[v] ) continue;
ans[v] = ans[u] + e[i].w;
dfs( v );
}
return;
}
void add( int u , int v , int w )
{
tot ++;
e[tot].v = v;
e[tot].w = w;
e[tot].nxt = hd[u];
hd[u] = tot;
return;
}
signed main()
{
int u,v,w;
cin >> n >> m;
for( int i = 1 ; i <= m ; i ++ )
{
cin >> u >> v >> w;
add( u , v , w );
add( v , u , -w );
}
for( int i = 1 ; i <= n ; i ++ )
if( !vis[i] )
dfs( i );
for( int i = 1 ; i <= n ; i ++ )
cout << ans[i] << ' ';
return 0;
}
我们会走到一起的。