Task
有n个正整数X1,X2,…,Xn,再给出m1+m2个限制条件,限制分为两类:
1. 给出a,b (1<=a,b<=n),要求满足Xa + 1 = Xb
2. 给出c,d (1<=c,d<=n),要求满足Xc <= Xd
在满足所有限制的条件下,求集合{Xi}大小的最大值。
2<=n<=600, 1<=m1+m2<=100,000.
Solution
1.差分约束:
把Xa+1=Xb转化为Xa+1<=Xb,Xa+1>=Xb,建一条a到b边权为1的边,b到a权值为-1的边.
把Xc<=Xd转化为 Xc-Xd<=0,建一条d到c边权为0的有向边.
建图之后就可以把问题转化为求图中任意两点最短路的最大值.
2.强连通缩点:
图中显然有环,通过强连通转化为DAG,考虑每个强联通分量之间的关系:
假如有一条从a到b的边,说明Xa>=Xb,但两者之间没有具体大小关系,那么我们就能保证存在一组解使得a,b的点权值不重复,那么最终的答案就是每个强连通分量的答案之和.
3.求最短路的最大值:
图中显然可能出现负环,那么就不能用dijkstra算法求最短路.我们可以通过Floyd或者SPFA判断是否有负环,若存在负环,即无解,否则求最短路.
Code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int M=200005;
const int N=605;
int head[N],scc=0,ec=0,n,m1,m2,dfn[N],low[N],Clock=0,stk[N],top=0,id[N],p[N],sum[N][N];
int dis[N][N],inq[N],cnt[N],Q[M*10];
struct node{
int to,v,nex;
}e[M<<1];
inline void rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),c>=48);
}
void ins(int a,int b,int c){
e[ec]=(node){b,c,head[a]};
head[a]=ec++;
}
void Tarjan(int x){
dfn[x]=low[x]=++Clock;
stk[++top]=x;
inq[x]=1;
for(int i=head[x];~i;i=e[i].nex){
int y=e[i].to;
if(!dfn[y]){
Tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(inq[y])low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
scc++;
while(1){
sum[scc][0]++;
sum[scc][sum[scc][0]]=stk[top];
inq[stk[top]]=0;
id[stk[top--]]=scc;
if(stk[top+1]==x)break;
}
}
}
int Find_(int k){
int l=0,r=0,i,j,mipath=0;
Q[r++]=p[k];
int s=p[k];
dis[s][s]=0;
while(l<r){
int x=Q[l++];
cnt[x]++;
inq[x]=0;
if(cnt[x]>sum[k][0])return -1;
for(i=head[x];~i;i=e[i].nex){
int y=e[i].to;
if(id[y]!=k)continue;
if(dis[s][y]>dis[s][x]+e[i].v){
dis[s][y]=dis[s][x]+e[i].v;
if(!inq[y]){
Q[r++]=y;
inq[y]=1;
}
}
}
}
for(i=1;i<=sum[k][0];i++){
mipath=max(mipath,dis[s][sum[k][i]]);
}
return mipath;
}
int solve(){
int i,ans=0;
for(i=1;i<=n;i++){
if(!dfn[i])Tarjan(i);
}
for(i=1;i<=scc;i++){
int b=0;
for(int j=1;j<=sum[i][0];j++){
p[i]=sum[i][j];
for(int k=1;k<=sum[i][0];k++){
int y=sum[i][k];
cnt[y]=0,inq[y]=0,dis[p[i]][y]=2e9;
}
int a=Find_(i);
if(a==-1)return -1;
b=max(b,a);
}
ans+=b+1;
}
return ans;
}
int main(){
memset(head,-1,sizeof(head));
int i,j,a,b,c,k;
rd(n);rd(m1);rd(m2);
for(i=1;i<=m1;i++){
rd(a);rd(b);//a+1=b,-> a+1<=b,a+1>=b -> a-b<=-1,b-a<=1 a->b 1
ins(a,b,1);
ins(b,a,-1);
}
for(i=1;i<=m2;i++){
rd(a);rd(b);//b>=a a-b<=0
ins(b,a,0);
}
int res=solve();
if(~res)printf("%d\n",res);
else puts("NIE");
return 0;
}