【Bellman-Ford求负环】UOJ32 [UR #2] 跳蚤公路

本文解析了UOJ上的一道趣题,涉及到Bellman-Ford算法的应用于图论中的负环检测。通过引入一个变量调整边权,探讨了如何判断在特定条件下是否存在发财路径。文章详细解释了解题思路,包括状态定义、负环判断条件及求解过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目】
UOJ
一幅 n n n个点 m m m条有向带边权 w i w_i wi的图,每条边还有一个红绿白三色之一。
称一条从 1 1 1开始,最后停在某个城市 v v v的路径为发财路径当且仅当路径上存在负环。
现在可以选定一个数 − 1 0 30 ≤ x ≤ 1 0 30 -10^{30}\leq x \leq 10^{30} 1030x1030,将每条红色边加上这个权,每条绿色边减去这个权。
问对于所有的 v v v,有多少个 x x x可以使得不存在发财路径,若数量大于 1 0 18 10^{18} 1018输出 − 1 -1 1

n ≤ 100 , m ≤ 10000 , ∣ w i ∣ ≤ 1 0 9 n\leq 100,m\leq 10000,|w_i|\leq 10^9 n100,m10000,wi109

【解题思路】
雅礼的时候趣题选讲讲过,然而并没有怎么听懂,对于 Bellman-Ford \text{Bellman-Ford} Bellman-Ford这个算法并不是很熟悉。

这个算法的大概思路是设 f i , j f_{i,j} fi,j表示走了 i i i步到达 j j j的最短路长度,若 f n , j &lt; f n − 1 , j f_{n,j}&lt;f_{n-1,j} fn,j<fn1,j,则节点 j j j在负环上。

对于此题由于有一个加权,不妨多加一维,设 f i , j , k f_{i,j,k} fi,j,k表示走了 i i i步,到达 j j j x x x的系数为 k k k,所需的最短距离。那么我们可以得到出现负环的条件:
k x + f n , i , k &lt; j x + f n − 1 , i , j kx+f_{n,i,k}&lt;jx+f_{n-1,i,j} kx+fn,i,k<jx+fn1,i,j
由于一个负环影响整个连通块,那么对于每个连通块中的点,可行的 x x x需要满足:
min ⁡ { k x + f n , i , k } ≥ min ⁡ { j x + f n , i , j } \min \{ kx+f_{n,i,k}\}\geq \min \{ jx+f_{n,i,j}\} min{kx+fn,i,k}min{jx+fn,i,j}
要解这个不等式,首先对于每个 k k k,枚举所有 j j j,求出下面解集的补集:
k x + f n , i , k ≥ min ⁡ { j x + f n , i , j } kx+f_{n,i,k} \geq \min \{ jx+f_{n,i,j}\} kx+fn,i,kmin{jx+fn,i,j}
然后对于所有补集求并后再求补集即可。

最后解集的形式一定为 ( − ∞ , t ] , [ l , r ] , [ t , + ∞ ) (-\infty,t],[l,r],[t,+\infty) (,t],[l,r],[t,+)中的一种(因为对于一个环有负环时满足条件的 x x x取值一定可以取到一边的极限)

复杂度大概是 O ( n 3 ) O(n^3) O(n3)

【参考代码】(是抄的)

#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;

typedef long long ll;
typedef pair<ll,ll> pii;
const int N=105,M=10005;
const ll inf=1e18;
int n,m;
bool mp[N][N];
ll f[N][N][N<<1];
vector<pii>vec[N],st;

struct edge{int u,v,w,s;}e[M];
ll Ceil(ll a,ll b);
ll Floor(ll a,ll b)
{
	if(a<0)a=-a,b=-b;
	if(b<0)return -Ceil(a,-b);
	return a/b;
}
ll Ceil(ll a,ll b)
{
	if(a<0)a=-a,b=-b;
	if(b<0)return -Floor(a,-b);
	return (a-1)/b+1;
}
void gmin(ll &x,ll y){x=min(x,y);}
void gmax(ll &x,ll y){x=max(x,y);}

void init()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) mp[i][i]=1;
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].s);
		mp[e[i].u][e[i].v]=1;
	}
	for(int k=1;k<=n;++k) for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
		mp[i][j]|=mp[i][k]&mp[k][j];
}
void bellmanford()
{
	memset(f,0x3f,sizeof(f));f[0][1][n]=0;
	for(int i=1;i<=n;++i) for(int k=0;k<=2*n;++k)
	{
		for(int j=1;j<=n;++j) f[i][j][k]=f[i-1][j][k];
		for(int j=1;j<=m;++j) if(k-e[j].s>=0 && k-e[j].s<=2*n)
			gmin(f[i][e[j].v][k],f[i-1][e[j].u][k-e[j].s]+e[j].w);
	}
}
void solve()
{
	for(int i=1;i<=n;++i) for(int j=0;j<=2*n;++j) if(f[n][i][j]<inf)
	{
		ll l=-inf,r=inf;
		for(int k=0;k<=2*n;++k) if(f[n-1][i][k]<inf)
		{
			if(j==k){if(f[n-1][i][j]==f[n][i][j])r=-inf,l=inf;}
			else if(j<k) gmax(l,Floor(f[n-1][i][k]-f[n][i][j],j-k)+1);
			else gmin(r,Ceil(f[n-1][i][k]-f[n][i][j],j-k)-1);
		}
		if(l<=r) vec[i].pb(mkp(l,r));
	}
	for(int i=1;i<=n;++i)
	{
		st.clear();
		for(int j=1;j<=n;++j) if(mp[j][i]) 
			{for(int k=0;k<(int)vec[j].size();++k) st.pb(vec[j][k]);}
		sort(st.begin(),st.end());
		ll l=-inf,r=-inf-1,sum=2*inf+1;
		for(int j=0;j<(int)st.size();++j)
			if(st[j].fi<=r) gmax(r,st[j].se);
			else sum-=r-l+1,l=st[j].fi,r=st[j].se;
		sum-=r-l+1;
		if(sum>=inf/100) puts("-1"); else printf("%lld\n",sum);
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("UOJ32.in","r",stdin);
	freopen("UOJ32.out","w",stdout);
#endif
	init();bellmanford();solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值