zoukankan      html  css  js  c++  java
  • 【agc004f】Namori Grundy

    那个问一下有人可以解释以下这个做法嘛,看不太懂QwQ~


    Description

    有一个n个点n条边的有向图,点的编号为从1到n。

    给出一个数组p,表明有(p1,1),(p2,2),…,(pn,n)这n条单向边,这n条边必定构成弱连通图。

    每个点均有一个权值ai,满足以下性质:

    (1)所有ai均为非负整数;

    (2)对于任意边(i,j),有ai≠aj

    (3)对于任意i,x(0≤x<ai),均有(i,j)满足aj=ai

    判断这样的图是否存在。(“POSSIBLE”/“IMPOSSIBLE”)


    Solution

    (早上花了三个小时还打挫了,心态爆炸)

    弱连通图:若该有向图所有边为双向边时,满足该图为连通图,则该有向图为弱连通图。

    我们容易发现,当一个点的出度为0时,它的权值也为0。我们可以对每一条边建反向边,然后进行拓扑排序,每次对新图中入度为0的点求出权值,然后删去。

    若最后有剩余的点,由于原图中每个点的入度均为1,则这些点形成一个环,取其中任意一个点开始遍历即可。特别地,若原图n个点构成环,则偶环存在而奇环不存在。

    下面讲一下如何求出每个点的权值:

    拓扑排序:

    若该点在原图中为叶子节点,则权值为0;

    若不为叶子节点,则权值为原图子节点权值中未出现的数的最小值。

    环:

    记录原图中该点不在环上的子节点权值中未出现的数的最小值a与次小值b。若该点权值为a,则下一点无限制;若该点权值为b,则下一点权值必为a。在跑环的时候,注意判断相邻两点权值不相等以及子节点权值满足条件(2)(3)即可。

    Code

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 #include<queue>
      5 #include<stack>
      6 using namespace std;
      7 #define next _next
      8 struct edge{
      9     int to,next;
     10 }e[200010],g[200010];
     11 int n,ehead[200010],ghead[200010];
     12 int m=0,a[200010]={0},out[200010]={0};
     13 int val[200010];
     14 bool vis[200010]={false};
     15 queue<int>q;
     16 stack<int>s[200010];
     17 bool dfs(int u,int w,int cannot){
     18     for(int i=ehead[u];~i;i=e[i].next)
     19         if(vis[e[i].to])
     20             s[val[e[i].to]].push(u);
     21     int v=-1;
     22     for(int i=ehead[u];~i;i=e[i].next)
     23         if(!vis[e[i].to]){
     24             v=e[i].to;
     25             break;
     26         }
     27     if(v==-1){
     28         if(w==-1){
     29             for(int i=0;;i++)
     30                 if(s[i].top()!=u){
     31                     val[u]=i;
     32                     break;
     33                 }
     34         }
     35         else{
     36             val[u]=w;
     37             for(int i=0;i<w;i++)
     38                 if(s[i].top()!=u){
     39                     for(int i=ehead[u];~i;i=e[i].next)
     40                         if(vis[e[i].to])
     41                             s[val[e[i].to]].pop();
     42                     return false;
     43                 }
     44         }
     45         bool ret=(val[u]!=cannot&&s[val[u]].top()!=u);
     46         for(int i=ehead[u];~i;i=e[i].next)
     47             if(vis[e[i].to])
     48                 s[val[e[i].to]].pop();
     49         return ret;
     50     }
     51     if(w==-1){
     52         int flag=-1;
     53         bool ret=false;
     54         for(int i=0;;i++)
     55             if(s[i].top()!=u){
     56                 vis[u]=true;
     57                 if(i!=cannot)
     58                     ret|=dfs(v,flag,val[u]=i);
     59                 vis[u]=false;
     60                 if(flag>-1)
     61                     break;
     62                 flag=i;
     63             }
     64         for(int i=ehead[u];~i;i=e[i].next)
     65             if(vis[e[i].to])
     66                 s[val[e[i].to]].pop();
     67         return ret;
     68     }
     69     int flag=-1;
     70     for(int i=0;i<w;i++)
     71         if(s[i].top()!=u){
     72             if(flag>-1){
     73                 for(int i=ehead[u];~i;i=e[i].next)
     74                     if(vis[e[i].to])
     75                         s[val[e[i].to]].pop();
     76                 return false;
     77             }
     78             flag=i;
     79         }
     80     bool ret=(w!=cannot&&s[w].top()!=u&&dfs(v,flag,val[u]=w));
     81     for(int i=ehead[u];~i;i=e[i].next)
     82         if(vis[e[i].to])
     83             s[val[e[i].to]].pop();
     84     return ret;
     85 }
     86 int main(){
     87     memset(ehead,-1,sizeof(ehead));
     88     memset(ghead,-1,sizeof(ghead));
     89     memset(val,-1,sizeof(val));
     90     while(!q.empty())q.pop();
     91     scanf("%d",&n);
     92     for(int i=0;i<=n;i++){
     93         while(!s[i].empty())
     94             s[i].pop();
     95         s[i].push(0x3f3f3f3f);
     96     }
     97     for(int i=1,x;i<=n;i++){
     98         scanf("%d",&x);
     99         e[i]=(edge){i,ehead[x]};
    100         g[i]=(edge){x,ghead[i]};
    101         ehead[x]=ghead[i]=i;
    102         a[x]++;out[x]++;
    103     }
    104     for(int i=1;i<=n;i++)
    105         if(out[i]==0){
    106             vis[i]=true;
    107             q.push(i);
    108         }
    109     while(!q.empty()){
    110         int u=q.front();
    111         q.pop();m++;
    112         for(int i=ehead[u];~i;i=e[i].next)
    113             s[val[e[i].to]].push(u);
    114         for(int i=0;;i++)
    115             if(s[i].top()!=u){
    116                 val[u]=i;
    117                 break;
    118             }
    119         for(int i=ehead[u];~i;i=e[i].next)
    120             s[val[e[i].to]].pop();
    121         for(int i=ghead[u];~i;i=g[i].next)
    122             out[g[i].to]--;
    123         for(int i=ghead[u];~i;i=g[i].next)
    124             if(out[g[i].to]==0){
    125                 vis[g[i].to]=true;
    126                 q.push(g[i].to);
    127             }
    128     }
    129     if(m==n){
    130         puts("POSSIBLE");
    131         return 0;
    132     }
    133     if(m==0){
    134         puts(n&1?"IMPOSSIBLE":"POSSIBLE");
    135         return 0;
    136     }
    137     for(int i=1;i<=n;i++)
    138         if(!vis[i]){
    139             puts(dfs(i,-1,-1)?"POSSIBLE":"IMPOSSIBLE");
    140             return 0;
    141         }
    142     return 0;
    143 }

    (话说环套树的题是真的烦[○・`Д´・ ○])

  • 相关阅读:
    使用transfor让图片旋转
    block与inline,inline和inline-block,块级和行内元素,行内替换和行内非替换元素
    使用classList来实现两个按钮样式的切换
    关于css透明度的问题
    级动(两级联动)思路分享
    js图片预览(一张图片预览)
    js关于密码框强弱度的提示
    javascript对象与实例
    javascript 逻辑运算符
    Raphael画圆弧
  • 原文地址:https://www.cnblogs.com/gzez181027/p/agc004f.html
Copyright © 2011-2022 走看看