BZOJ 1194 [HNOI2006]潘多拉的盒子 (图论+拓扑排序+tarjan)
题面:洛谷传送门 BZOJ传送门
标签里三个算法全都是提高组的,然而..这是一道神题
我们把这道题分为两个部分解决
1.找出所有咒语机两两之间的包含关系
2.求出咒语机的最长上升序列
我们假设咒语机$a,b$满足$a\in b$
如果这个条件不成立,说明存在一个串$S$,$a$能输出,$b$不能输出
一个咒语机能产生的字符串可能是无限长的,直接枚举字符串肯定不行
考虑转化问题
我们构造另外一个图,图中每个点是一个二元组$(x,y)$
我们暴力枚举咒语机$a$中的一个元件$x$,$b$中的一个元件$y$
我们把$(x,y)$分别向$(p_{x,0},p_{y,0}),(p_{x,1},p_{y,1})$连边
如果从$(0,0)$开始,走到了一个节点$(x,y)$,且$x$是$S$的一个输出点,而$y$却不是
说明$a\in b$不成立
因为从$(0,0)$走到$(x,y)$这样一条路径,对于$a,b$来说,是它们都能表示出来的字符串
即$a$中从$0$号元件走到$x$,$b$中从$0$号元件走到$y$,它们走出来的路径表示的字符串相同
而$a$能把它输出,$b$却不能,那么一定不满足$a\in b$
我们解决了第一个部分
我们得到了咒语机(点的集合)之间的关系,现在我们要求出它的最长上升序列
拓扑排序裸题吧
然而可能会出现环,即某些咒语机能表示出来的字符串集合相同
用$tarjan$缩点重新建出拓扑图,拓扑排序跑最长路即可
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N1 2505 5 #define M1 55 6 using namespace std; 7 8 struct Graph{ 9 int p[M1][2],out[M1],n,m; 10 void Read() 11 { 12 int i,x; 13 scanf("%d%d",&n,&m); 14 for(i=1;i<=m;i++) scanf("%d",&x),out[x+1]=1; 15 for(i=1;i<=n;i++) scanf("%d%d",&p[i][0],&p[i][1]),p[i][0]++,p[i][1]++; 16 } 17 }g[M1]; 18 struct Edge{ 19 int head[N1],nxt[N1<<1],to[N1<<1],cte; 20 void ae(int u,int v){ cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; } 21 }e,E,N; 22 23 int nt[N1],que[N1],hd,tl,vis[N1],inc[M1]; 24 void init() 25 { 26 memset(&e,0,sizeof(e)); 27 memset(nt,0,sizeof(nt)); 28 memset(vis,0,sizeof(vis)); 29 } 30 int solve(Graph &ss,Graph &tt) 31 { 32 int i,j,x,v,ans=-2; init(); 33 for(i=1;i<=ss.n;i++) 34 for(j=1;j<=tt.n;j++) 35 { 36 x=(i-1)*tt.n+j; 37 v=(ss.p[i][0]-1)*tt.n+tt.p[j][0]; e.ae(x,v); 38 v=(ss.p[i][1]-1)*tt.n+tt.p[j][1]; e.ae(x,v); 39 if(ss.out[i]&&!tt.out[j]) nt[x]=1; 40 if(!ss.out[i]&&tt.out[j]) nt[x]=-1; 41 } 42 hd=1,tl=0; que[++tl]=1; vis[1]=1; 43 while(hd<=tl) 44 { 45 x=que[hd++]; 46 if(nt[x]) 47 { 48 if(ans==-2) ans=nt[x]; 49 else if(ans!=nt[x]) return 0; 50 } 51 for(j=e.head[x];j;j=e.nxt[j]) 52 { 53 v=e.to[j]; 54 if(!vis[v]) que[++tl]=v, vis[v]=1; 55 } 56 } 57 return ans; 58 } 59 int low[M1],dfn[M1],use[M1],stk[M1],tim,tp; 60 int num[M1],nn,dad[M1],f[M1]; 61 void tarjan(int x,int fa) 62 { 63 int j,v; 64 low[x]=dfn[x]=++tim; 65 stk[++tp]=x; use[x]=1; 66 for(j=E.head[x];j;j=E.nxt[j]) 67 { 68 v=E.to[j]; 69 if(v==fa) continue; 70 if(!dfn[v]){ 71 tarjan(v,x); 72 low[x]=min(low[x],low[v]); 73 }else if(use[v]){ 74 low[x]=min(low[x],dfn[v]); 75 } 76 } 77 if(low[x]==dfn[x]) 78 { 79 nn++; 80 while(stk[tp]!=x) 81 { 82 use[stk[tp]]=0,dad[stk[tp]]=nn; 83 tp--,num[nn]++; 84 } 85 dad[x]=nn, use[x]=0, tp--, num[nn]++; 86 } 87 } 88 89 int S; 90 91 int main() 92 { 93 scanf("%d",&S); 94 int i,j,k,s,sx,sy,x,v,ans=0; 95 for(s=1;s<=S;s++) g[s].Read(); 96 for(sx=1;sx<=S;sx++) 97 for(sy=sx+1;sy<=S;sy++) 98 { 99 k=solve(g[sx],g[sy]); 100 if(!k) continue; 101 if(ans==-2) E.ae(sx,sy), E.ae(sy,sx); 102 else if(k==-1) E.ae(sx,sy); 103 else E.ae(sy,sx); 104 } 105 for(s=1;s<=S;s++) 106 if(!dfn[s]) tarjan(s,-1); 107 for(x=1;x<=S;x++) 108 for(j=E.head[x];j;j=E.nxt[j]) 109 { 110 v=E.to[j]; 111 if(dad[x]!=dad[v]) 112 N.ae(dad[x],dad[v]), inc[dad[v]]++; 113 } 114 hd=1,tl=0; 115 for(i=1;i<=nn;i++) if(!inc[i]) que[++tl]=i; 116 memset(use,0,sizeof(use)); 117 while(hd<=tl) 118 { 119 x=que[hd++]; ans=max(ans,f[x]); 120 for(j=N.head[x];j;j=N.nxt[j]) 121 { 122 v=N.to[j]; 123 f[v]=max(f[v],f[x]+1); inc[v]--; 124 if(!inc[v]) que[++tl]=v; 125 } 126 } 127 printf("%d\n",ans+1); 128 }