• BZOJ 1194 [HNOI2006]潘多拉的盒子 (图论+拓扑排序+tarjan)


    题面:洛谷传送门 BZOJ传送门

    标签里三个算法全都是提高组的,然而..这是一道神题

    我们把这道题分为两个部分解决

    1.找出所有咒语机两两之间的包含关系

    2.求出咒语机的最长上升序列

    我们假设咒语机$a,b$满足$ain 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$却不是

    说明$ain b$不成立

    因为从$(0,0)$走到$(x,y)$这样一条路径,对于$a,b$来说,是它们都能表示出来的字符串

    即$a$中从$0$号元件走到$x$,$b$中从$0$号元件走到$y$,它们走出来的路径表示的字符串相同

    而$a$能把它输出,$b$却不能,那么一定不满足$ain 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
    ",ans+1);
    128 }
  • 相关阅读:
    仿jquery 选择器功能
    多个div拖拽功能
    js 模拟jquery onready 事件
    随着鼠标移动的图片百叶窗效果
    计算体重引发的思考
    js 模拟事件
    表单验证功能(利用冒泡功能)
    视频播放滚动条(最终完善版)
    仿制视频播放滚动条效果(加左右控制按钮)
    无极树(待整理)
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10305458.html
Copyright © 2020-2023  润新知