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;
}