观察发现,只有经过负环才能“发财”。
于是有一个思路。枚举与所求点 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(n−1,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(n−1,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(n−1,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)+kx≥min{g(n−1,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;
}