转载请注明出处:
http://www.cnblogs.com/hzoi-wangxh/p/7738628.html
[Poi2012]Festival
时间限制: 1 Sec 内存限制: 64 MB题目描述
有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}大小的最大值。
输入
第一行三个正整数n, m1, m2 (2<=n<=600, 1<=m1+m2<=100,000)。
接下来m1行每行两个正整数a,b (1<=a,b<=n),表示第一类限制。
接下来m2行每行两个正整数c,d (1<=c,d<=n),表示第二类限制。
输出
一个正整数,表示集合{Xi}大小的最大值。
如果无解输出NIE。
样例输入
4 2 21 23 41 43 1
样例输出
3
提示
|X3=1, X1=X4=2, X2=3
这样答案为3。容易发现没有更大的方案。
solution
首先想到差分约束。对于限制2,Xc<=Xd,我们可以把它导成Xc-Xd<=0,由d向c建一条边权为0的边。对于限制1,我们可以转换成Xa+1<=Xb Xa+1>=Xb,然后再导成Xa-Xb<=-1 Xb-Xa<=1,由b向a建一条边权为-1的边,由a向b建一条边权为1的边。注意如果之前已经在两点之间建过边,边权取最小值。
然后我们开始在图上跑。由于需要满足条件,我们跑最短路。先用tarjan缩点,在每一个环里跑两点之间距离的最小值。如果跑出来发现dis[i][i]不为0,说明不能满足条件。如果能满足,取每个环里两点之间距离绝对值的最大值,相加即为集合最大的大小。
附上代码
#include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; struct tree{ int u,v,next,d; }l[301000]; int n,m1,m2,lian[605],e,dis[605][605]; int low[605],dfn[605],num,fa[605],cnt,head,st[605]; bool pd[605]; void bian(int,int,int); void tarjan(int); int mmin(int x,int y) { if(x>y) return y; return x; } int mmax(int x,int y) { if(x>y) return x; return y; } int main() { //freopen("in.txt","r",stdin); scanf("%d%d%d",&n,&m1,&m2); memset(dis,30,sizeof(dis)); for(int i=1;i<=m1;i++) { int x,y; scanf("%d%d",&x,&y); bian(y,x,-1); bian(x,y,1); dis[y][x]=mmin(dis[y][x],-1); dis[x][y]=mmin(dis[x][y],1); } for(int i=1;i<=m2;i++) { int x,y; scanf("%d%d",&x,&y); bian(y,x,0); dis[y][x]=mmin(dis[y][x],0); } for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i); for(int i=1;i<=n;i++) dis[i][i]=0; for(int i=1;i<=cnt;i++) for(int j=1;j<=n;j++) { if(fa[j]!=i) continue; for(int k=1;k<=n;k++) { if(fa[k]!=i) continue; for(int l=1;l<=n;l++) { if(fa[l]!=i) continue; dis[k][l]=mmin(dis[k][l],dis[k][j]+dis[j][l]); } } } bool cun=0; for(int i=1;i<=n;i++) if(dis[i][i]!=0) { cun=1;break; } if(cun==1) { printf("NIE"); return 0; } else { int zui=0; for(int i=1;i<=cnt;i++) { int mx=0; for(int j=1;j<=n;j++) { if(fa[j]!=i) continue; for(int k=1;k<=n;k++) { if(fa[k]!=i) continue; mx=mmax(abs(dis[j][k]),mx); } } zui+=mx; ++zui; } printf("%d",zui); } return 0; } void bian(int x,int y,int z) { e++; l[e].u=x; l[e].v=y; l[e].d=z; l[e].next=lian[x]; lian[x]=e; } void tarjan(int x) { ++num; dfn[x]=num; low[x]=num; pd[x]=1; st[++head]=x; for(int i=lian[x];i;i=l[i].next) { int v=l[i].v; if(dfn[v]==0) { tarjan(v); low[x]=mmin(low[x],low[v]); } else if(pd[v]==1) low[x]=mmin(dfn[v],low[x]); } if(dfn[x]==low[x]) { cnt++; while(1) { int tmp=st[head];head--; fa[tmp]=cnt; pd[tmp]=0; if(tmp==x) break; } } }