[UOJ #32][UR #2]跳蚤公路 解题报告

观察发现,只有经过负环才能“发财”。

于是有一个思路。枚举与所求点 u u u 连通的点 v v v,判断产生负环时 x x x 的取值范围。而关于 x x x 的不等式显然为一次不等式。

这时候的思维瓶颈是如何枚举或判断负环。如果熟悉最短路算法,会想到Bellman-Ford与SPFA算法都判断图是否有负环。这里我们只讲解Bellman-Ford的做法。

Bellman-Ford判断负环的原理是这样的。设 f ( i , j ) f(i,j) f(i,j) 为移动了 i i i 步到达节点 j j j 的最短路径长度。则根据 Bellman-Ford算法,如果 f ( n , j ) < f ( n − 1 , j ) f(n,j)<f(n-1,j) f(n,j)<f(n1,j),则节点 j j j 在负权环上。

由于一部分边和 x x x 有关,不妨设 g ( i , j , k ) g(i,j,k) g(i,j,k) 为移动了 i i i 步,到达节点 j j j x x x 的系数为 k k k x = 0 x=0 x=0 时的最短路径长度。

显然有
f ( i , j ) = m i n { g ( i , j , k ) + k x } f(i,j)=min\{g(i,j,k)+kx\} f(i,j)=min{g(i,j,k)+kx}
那么对于节点 u u u u u u 不在负环上当且仅当
f ( n , u ) ≥ f ( n − 1 , u ) f(n,u)\ge f(n-1,u) f(n,u)f(n1,u)

m i n { g ( n , u , k ) + k x } ≥ m i n { g ( n − 1 , u , j ) + j x } min\{g(n,u,k)+kx\}\ge min\{g(n-1,u,j)+jx\} min{g(n,u,k)+kx}min{g(n1,u,j)+jx}
解不等式即可。

对于每一个 k k k,枚举 j j j 求下式解集的补集
g ( n , u , k ) + k x ≥ m i n { g ( n − 1 , u , j ) + j x } g(n,u,k)+kx\ge min\{g(n-1,u,j)+jx\} g(n,u,k)+kxmin{g(n1,u,j)+jx}
将所有补集求并后再求补集即可。

关于解集一定为 ( − ∞ , t ] (-\infty,t] (,t] [ l , r ] [l,r] [l,r] [ t , + ∞ ) [t,+\infty) [t,+) 的形式,证明如下。

因为对于一个环,它为负环时满足条件的 x x x 的取值范围一定是 ( − ∞ , t ] (-\infty,t] (,t] [ t , + ∞ ) [t,+\infty) [t,+) 的形式。所以求交后的解集一定是以上三种形式。

#include
#include
#include
#include
#define reg register
#define dmin(_,__) ((_)<(__)?(_):(__))
#define dmax(_,__) ((_)>(__)?(_):(__))
#define f(i,j,k) f[i][j][k+102]
const long long inf=1e18;
int n,m;
struct edge{
	int u,v,w,k;
}e[10010];
struct data{long long lwb,upb;};
inline bool operator<(reg const data&x,reg const data&y){
	return (x.lwb==y.lwb)?(x.upb<y.upb):(x.lwb<y.lwb);
}
std::vector<data>a[110];
long long f[110][110][310];
bool g[110][110];
long long floor(reg long long x,reg long long y){
	if(y<0)x=-x,y=-y;
	return x>0?x/y:-((-x+y-1)/y);
}
long long ceil(reg long long x,reg long long y){
	return -floor(-x,y);
}
/*
	Floyd.
*/
void floyd(){
	for(reg int i=1;i<=m;++i)
		g[e[i].u][e[i].v]=1;
	for(reg int i=1;i<=n;++i)
		g[i][i]=1;
	for(reg int k=1;k<=n;++k)
		for(reg int i=1;i<=n;++i)
			for(reg int j=1;j<=n;++j)
				g[i][j]|=(g[i][k]&g[k][j]);
	return;
}
/*
	Bellman-Ford.
*/
void bf(){
	for(reg int i=0;i<=n;++i)
		for(reg int j=1;j<=n;++j)
			for(reg int k=-n;k<=n;++k)
				f(i,j,k)=inf;
	f(0,1,0)=0;
	for(reg int i=1;i<=n;++i){
		memcpy(f[i],f[i-1],sizeof(f[i]));
		for(reg int j=1;j<=m;++j){
			int u=e[j].u,v=e[j].v,w=e[j].w,s=e[j].k;
			for(reg int k=-n;k<=n;++k)
				if(f(i-1,u,k)<inf)
					f(i,v,k+s)=dmin(f(i,v,k+s),f(i-1,u,k)+w);
		}
	}
	return;
}
void sol(){
	bool flag=0;
	for(reg int i=1;i<=n;++i)
		for(reg int k=-n;k<=n;++k){
			if(f(n,i,k)==inf)continue;
			flag=0;
			long long lwb=-inf,upb=inf,tmp;
			for(reg int j=-n;j<=n;++j){
				if(f(n-1,i,j)==inf)continue;
				if(k>j){
					tmp=ceil(f(n-1,i,j)-f(n,i,k),k-j); //ceil
					upb=dmin(upb,tmp);
				}
				else if(k<j){
					tmp=floor(f(n-1,i,j)-f(n,i,k),k-j); //floor
					lwb=dmax(lwb,tmp);
				}
				else if(f(n,i,k)>=f(n-1,i,j)){
					flag=1;
					break;
				}
			}
			if(!flag&&lwb<upb)a[i].push_back((data){lwb,upb});
		}
	return;
}
data b[40010];int tB;
void getans(){
	bool flag;
	for(reg int i=1;i<=n;++i){
		tB=0;
		for(reg int j=1;j<=n;++j)
			if(g[1][j]&&g[j][i])
				for(reg int k=0;k<a[j].size();++k)
					b[++tB]=a[j][k];
		std::sort(b+1,b+tB+1);
		long long lwb=inf,upb=-inf,lst=-inf,ans;
		flag=0;
		for(reg int j=1;j<=tB;++j){
			if(j==1&&-inf<b[j].lwb){
				lwb=-inf;upb=b[j].lwb;
				flag=1;break;
			}
			if(lst!=-inf&&lst<=b[j].lwb){
				lwb=lst;upb=b[j].lwb;
				flag=1;break;
			}
			lst=dmax(lst,b[j].upb);
		}
		if(!flag&&lst<inf)lwb=lst,upb=inf;
		if(lwb==-inf||upb==inf||!tB)
			puts("-1");
		else{
			ans=dmax(upb-lwb+1,0);
			printf("%lld\n",ans);
		}
	}
	return;
}
int main(){
	scanf("%d%d",&n,&m);
	for(reg int i=1,u,v,w,k;i<=m;++i){
		scanf("%d%d%d%d",&u,&v,&w,&k);
		e[i]=(edge){u,v,w,k};
	}
	floyd();bf();
	sol();getans();
	return 0;
}

你可能感兴趣的:(图论)