UOJ32【UR #2】跳蚤公路

一个有向图,边权为(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;
}
原文地址:https://www.cnblogs.com/jz-597/p/14208882.html