UOJ32【UR #2】跳蚤公路
一个有向图,边权为\(w_i+s_ix\)的形式,其中\(s_i\in[-1,1]\),\(w_i\)可以为负数。
从\(1\)出发,对于每个点\(v\),问整数\(x\)的个数,使得:从\(1\)到\(v\)的过程中不会得到无限小的权值和(也就是说不存在某个负环,可以从\(1\)到达,可以到达\(v\))
\(n\le 100,m\le 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}+ax\ge \min_b g_{n-1,v,b}+bx\)。解不等式即可,求出\(v\)的贡献。
怎么解?显然等价于\(\forall a,\exist b, g_{n,v,a}+ax\ge 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)\n",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\n",tmp>INF/2?-1:tmp);
}
return 0;
}