一个有向图,边权为(w_i+s_ix)的形式,其中(s_iin[-1,1]),(w_i)可以为负数。
从(1)出发,对于每个点(v),问整数(x)的个数,使得:从(1)到(v)的过程中不会得到无限小的权值和(也就是说不存在某个负环,可以从(1)到达,可以到达(v))
(nle 100,mle 10000)
没想到Bellman-Ford居然有SPFA不能做的实际用途!
现在的主要问题是找到(x)什么取值的时候没有负环。
之前想了些做法但是它们都假掉了。另外提醒个看起来显然但是假了的结论:合法的(x)不一定连续。
正解考虑Bellman-Ford中判负环的方法。设(f_{i,v})表示经过至多(i)步后到达(v)点的最短距离。于是(f_{n,v}<f_{n-1,v})为(v)在负环内的充分不必要条件(因为如果不存在负环,最短路不会走环,所以第(n)步不会进行更新)。如果出现了这样的(v),那么它能到达的点都可以被贡献到。
于是考虑(f_{n,v}ge f_{n-1,v})的条件。设(g_{i,v,k})表示从经过至多(i)步后到达(v)点的,且(x)的系数为(k)的最短距离。
那么条件为:(min_a g_{n,v,a}+axge min_b g_{n-1,v,b}+bx)。解不等式即可,求出(v)的贡献。
怎么解?显然等价于(forall a,exist b, g_{n,v,a}+axge g_{n-1,v,b}+bx)。解了之后求个并再求个交。
维护解集的时候需要维护若干个区间,有些麻烦(尤其是没有意识到这一点的我调了好久)。
总时间复杂度(O(n^4))。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <list>
#define N 105
#define ll long long
#define INF 2000000000000000000
int n,m;
struct EDGE{
int to,w,s;
EDGE *las;
} e[N*N];
int ne;
EDGE *last[N];
void link(int u,int v,int w,int s){
e[ne]={v,w,s,last[u]};
last[u]=e+ne++;
}
ll g[N][N][N+N];
void upd(ll &x,ll y){
x=min(x,y);
}
struct itv{ll l,r;};
itv operator&(itv a,itv b){return {max(a.l,b.l),min(a.r,b.r)};}
itv operator|(itv a,itv b){return {min(a.l,b.l),max(a.r,b.r)};}
list<itv> s[N];
void merge(list<itv> &a,list<itv> b){
auto p=a.begin(),q=b.begin();
while (p!=a.end() && q!=b.end()){
if (p->l<q->l){
p->l=q->l;
if (p->l>p->r)
p=a.erase(p);
}
else if (p->l>q->l){
q->l=p->l;
if (q->l>q->r)
q=b.erase(q);
}
else{
if (q->r>p->r){
q->l=p->r+1;
++p;
}
else if (p->r>q->r){
a.insert(p,(itv){p->l,q->r});
p->l=q->r+1;
++q;
}
else
++p,++q;
}
}
for (;p!=a.end();p=a.erase(p));
}
int c[N][N];
int main(){
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i){
int u,v,w,s;
scanf("%d%d%d%d",&u,&v,&w,&s);
link(u,v,w,s);
}
memset(g[0],127,sizeof g[0]);
g[0][1][0 +N]=0;
for (int i=0;i<n;++i){
memcpy(g[i+1],g[i],sizeof g[i]);
for (int j=1;j<=n;++j)
for (int k=-i;k<=i;++k)
if (g[i][j][k+N]<INF)
for (EDGE *ei=last[j];ei;ei=ei->las){
// printf("(%d,%d,%d)<-(%d,%d,%d)
",i+1,ei->to,k+ei->s,i,j,k);
upd(g[i+1][ei->to][k+ei->s+N],g[i][j][k+N]+ei->w);
}
}
for (int v=1;v<=n;++v){
s[v].push_back((itv){-INF,INF});
for (int a=-n;a<=n;++a){
if (g[n][v][a+N]>=INF)
continue;
ll L=-INF-1,R=INF+1;
for (int b=-n;b<=n;++b){
if (g[n-1][v][b+N]>=INF)
continue;
int c=b-a;
if (c==0){
if (g[n][v][a+N]-g[n-1][v][b+N]>=0){
L=INF,R=-INF;
break;
}
}
else if (c>0)
L=max(L,(ll)floor((double)(g[n][v][a+N]-g[n-1][v][b+N])/c));
else
R=min(R,(ll)ceil((double)(g[n][v][a+N]-g[n-1][v][b+N])/c));
}
if (L>=R-1)
continue;
list<itv> t;
t.clear();
if (-INF<=L)
t.push_back((itv){-INF,L});
if (R<=INF)
t.push_back((itv){R,INF});
merge(s[v],t);
}
}
for (int i=1;i<=n;++i)
c[i][i]=1;
for (int i=1;i<=n;++i)
for (EDGE *ei=last[i];ei;ei=ei->las)
c[i][ei->to]=1;
for (int k=1;k<=n;++k)
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
c[i][j]|=c[i][k]&c[k][j];
for (int v=1;v<=n;++v){
list<itv> ans({(itv){-INF,INF}});
for (int u=1;u<=n;++u)
if (c[1][u] && c[u][v])
merge(ans,s[u]);
ll tmp=0;
for (auto p=ans.begin();p!=ans.end();++p)
tmp+=p->r-p->l+1;
printf("%lld
",tmp>INF/2?-1:tmp);
}
return 0;
}