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;
}
posted @ 2025-09-08 18:36  FormulaOne  阅读(4)  评论(0)    收藏  举报