题目大意
有一个n个点的环,相邻两个点距离是正整数。
现在有若干个约束,是以下两种其中之一:
1、S与T的顺时针距离不小于L。
2、S与T的顺时针距离不大于L。
问环总长的解的方案数。
差分约束
不妨设D[i]表示0与i的顺时针距离,特殊的,D[n]表示整个环的总长,设为X。
显然,
D[i+1]−D[i]>=1
,等同于
D[i]−D[i+1]≤−1
。
D[n]−D[0]=X
,拆成
D[n]−D[0]≤X
与
D[0]−D[n]≤−X
。
对于给定约束,显然也可以写成
D[a]−D[b]≤…
的形式。
于是我们可以建立一幅图,最短路对应一种约束最强的解,因此只有每个点均存在最短路即有解。
有些边权是未知数X,我们于是知道如何判定一个X是否合法,即原图是否产生负环,可以用最短路算法判定。
那么我们如何求有多少个X合法呢?
二分
显然,X的取值范围是一段区间。
说明是可二分的,难点是如何知道一个非法X是大了还是小了。
最短路算法可以获取图上一个负环关于X的系数和,只要知道其是正还是负,便知道X是大了还是小了。
Floyd
注意到每条边关于X的系数在[-1,1]内,合法情况下最短路不可能走重复边,可以发现任意一条路径关于X的系数和是O(m)级别。
不妨设f[i,j,k]表示从i到j的一条路径,路径上关于X的系数和为k,常数项最小是多少。
求出这个后,只要能保证任意f[i,i,s]+sX>=0即可,因此可以通过这个求出X的取值上下界。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=50+10;
const ll inf=10000000000000000;
ll f[maxn][maxn][200+10];
int i,j,k,l,r,s,t,x,y,n,m,tot,mx;
ll L,R;
class YamanoteLine{
void link(int x,int y,int kk,int bb){
f[x][y][kk+mx]=min(f[x][y][kk+mx],(ll)bb);
}
public:
ll howMany(int N,vector<int> S1,vector<int> T1,vector<int> L1,vector<int> S2,vector<int> T2,vector<int> L2){
n=N;
mx=n;
fo(i,0,n)
fo(j,0,n)
fo(k,0,mx)
f[i][j][k]=inf;
fo(i,0,n) f[i][i][mx]=0;
fo(i,0,n-1) link(i+1,i,0,-1);
link(n,0,-1,0);
link(0,n,1,0);
r=S1.size()-1;
fo(i,0,r){
s=S1[i];t=T1[i];l=L1[i];
if (s<t) link(t,s,0,-l);
else link(t,s,1,-l);
}
r=S2.size()-1;
fo(i,0,r){
s=S2[i];t=T2[i];l=L2[i];
if (s<t) link(s,t,0,l);
else link(s,t,-1,l);
}
fo(k,0,n)
fo(i,0,n)
fo(x,-mx,mx)
if (f[i][k][x+mx]<inf)
fo(j,0,n)
fo(y,max(-mx,-mx-x),min(mx,mx-x))
if (f[k][j][y+mx]<inf)
f[i][j][x+y+mx]=min(f[i][j][x+y+mx],f[i][k][x+mx]+f[k][j][y+mx]);
fo(i,0,n)
if (f[i][i][mx]<0) return 0;
L=n;R=inf;
fo(s,1,mx)
fo(i,0,n)
if (f[i][i][s+mx]<inf) L=max(L,(ll)(-f[i][i][s+mx]-1)/s+1);
fo(s,-mx,-1)
fo(i,0,n)
if (f[i][i][s+mx]<inf) R=min(R,(ll)f[i][i][s+mx]/(-s));
if (L>R) return 0;
else if (R==inf) return -1;
else return R-L+1;
}
};