BZOJ_2788_[Poi2012]Festival_差分约束+tarjan+floyed
BZOJ_2788_[Poi2012]Festival_差分约束+tarjan+floyed
Description
有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}大小的最大值。
Input
第一行三个正整数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),表示第二类限制。
Output
一个正整数,表示集合{Xi}大小的最大值。
如果无解输出NIE。
Sample Input
4 2 2
1 2
3 4
1 4
3 1
1 2
3 4
1 4
3 1
Sample Output
3
先正常差分约束建图,将得到的图缩点。
可以发现每个强连通分量之间互不影响,也就是可以直接加在一起。
然后强联通分量内部求一个最长路,最长路就是合法的方案中最大的数减最小的数的最大值,这个加一就是每个强联通分量的答案。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 650 #define M 200050 int head[N],to[M],nxt[M],cnt,n,val[M],S[N],dfn[N],low[N],ins[N],from[M],m1,m2; int bl[N],scc,f[N][N],tot,top; inline void add(int u,int v,int w) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w; from[cnt]=u; } void dfs(int x) { S[++top]=x; ins[x]=1; dfn[x]=low[x]=++tot; int i; for(i=head[x];i;i=nxt[i]) { if(!dfn[to[i]]) { dfs(to[i]); low[x]=min(low[x],low[to[i]]); }else if(ins[to[i]]) { low[x]=min(low[x],dfn[to[i]]); } } if(dfn[x]==low[x]) { int t=S[top--]; bl[t]=++scc; ins[t]=0; while(t!=x) { t=S[top--]; bl[t]=scc; ins[t]=0; } } } void floyd() { int i,j,k; for(k=1;k<=n;k++) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { f[i][j]=min(f[i][j],f[i][k]+f[k][j]); } } } } int main() { scanf("%d%d%d",&n,&m1,&m2); int i,x,y,j,k; for(i=1;i<=m1;i++) { scanf("%d%d",&x,&y); add(x,y,1); add(y,x,-1); } for(i=1;i<=m2;i++) { scanf("%d%d",&x,&y); add(y,x,0); } for(i=1;i<=n;i++) add(0,i,0); for(i=1;i<=n;i++) if(!dfn[i]) { dfs(i); } memset(f,0x3f,sizeof(f)); for(i=1;i<=cnt;i++) { f[from[i]][to[i]]=min(f[from[i]][to[i]],val[i]); } for(i=1;i<=n;i++) f[i][i]=0; int ans=0; floyd(); for(i=1;i<=n;i++) if(f[i][i]<0) { puts("NIE"); return 0; } for(i=1;i<=scc;i++) { int re=0; for(j=1;j<=n;j++) { for(k=1;k<=n;k++) { if(bl[j]==i&&bl[k]==i) { //printf("%d\n",f[j][k]); re=max(re,f[j][k]); } } } ans+=re+1; } printf("%d",ans); }