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;
}
posted @ 2020-12-29 21:38  jz_597  阅读(142)  评论(0编辑  收藏  举报