• 图论 公约数 找环和链 BZOJ [NOI2008 假面舞会]


    BZOJ 1064: [Noi2008]假面舞会

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 1655  Solved: 798
    [Submit][Status][Discuss]

    Description

    一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具。每个面具都有一个编号,主办方会把此编号告诉拿该面具的人。为了使舞会更有神秘感,主办方把面具分为k (k≥3)类,并使用特殊的技术将每个面具的编号标在了面具上,只有戴第i 类面具的人才能看到戴第i+1 类面具的人的编号,戴第k 类面具的人能看到戴第1 类面具的人的编号。 参加舞会的人并不知道有多少类面具,但是栋栋对此却特别好奇,他想自己算出有多少类面具,于是他开始在人群中收集信息。 栋栋收集的信息都是戴第几号面具的人看到了第几号面具的编号。如戴第2号面具的人看到了第5 号面具的编号。栋栋自己也会看到一些编号,他也会根据自己的面具编号把信息补充进去。由于并不是每个人都能记住自己所看到的全部编号,因此,栋栋收集的信 息不能保证其完整性。现在请你计算,按照栋栋目前得到的信息,至多和至少有多少类面具。由于主办方已经声明了k≥3,所以你必须将这条信息也考虑进去。

    Input

    第一行包含两个整数n, m,用一个空格分隔,n 表示主办方总共准备了多少个面具,m 表示栋栋收集了多少条信息。接下来m 行,每行为两个用空格分开的整数a, b,表示戴第a 号面具的人看到了第b 号面具的编号。相同的数对a, b 在输入文件中可能出现多次。

    Output

    包含两个数,第一个数为最大可能的面具类数,第二个数为最小可能的面具类数。如果无法将所有的面具分为至少3 类,使得这些信息都满足,则认为栋栋收集的信息有错误,输出两个-1。

    Sample Input

    【输入样例一】

    6 5
    1 2
    2 3
    3 4
    4 1
    3 5

    【输入样例二】

    3 3
    1 2
    2 1
    2 3

    Sample Output

    【输出样例一】
    4 4

    【输出样例二】
    -1 -1

    HINT

    100%的数据,满足n ≤ 100000, m ≤ 1000000。

    网上的代码+我的注解:

      1 /*
      2 1.当图中有环时,k必定是环长度的约数,那么答案就是全部环的最大公约数和最小的大于3的公约数
      3 (而且可以看出这个最大公约数一定是这个大于3的最小公约数的倍数,
      4 证明:假设真正的结果是m,因为最大公约数一定是n*m(n>=1),大于三的最小公约数一定是m的约数,
      5 所以这个最大公约数一定是这个大于3的最小公约数的倍数。可以用这个方法从最大值找到最小值),
      6 若最大公约数小于3则无解;
      7 2.当图中没有环时,最小值毫无疑问就是3了,k最大就是所有联通块最长链
      8 (假设每个联通块的最长链都可以接到一起,不能在一个联通块里找两条链,因为他们是有限制关系的,不能接起来)的和。
      9 3技巧:这里面有个技巧,因为如果有两个面具能看见同一个或一个能看见两个,那这两个的一定属于同一类,而且也有可能出现这样的联通块:1->2->3->4->5且6->7->5这样就变得不好处理了。可以把有向边换成无向边正向的话类数+1,反向的话类数-1。这样一来如果找到已经表过号的点就是找到了环,环的长度就是abs(将要编的号-已有编号)。而最长链就是一个联通块内最大编号-最小编号(因为有可能出现负数或0)。
     10 实现时,所有边建长度为1的正向边和长度为-1的反向边,会容易处理很多(这样可以将所有点都标记成到某点距离为多少,可以方便计算环的长度)。
     11 
     12 时间复杂度分析:
     13 标号的时间复杂度为O(n+m),枚举的时间复杂度是O(n),找公约数的时间为O(log(n)),所以总时间复杂度为O(nlog(n)+m).
     14 */
     15 #include <iostream>
     16 #include <cstdio>
     17 #include <cstring>
     18 #include <algorithm>
     19 #include <cstdlib>
     20 #include <cmath>
     21 #define N 200000
     22 #define M 4000000
     23 
     24 using namespace std;
     25 
     26 int head[N],next[M],to[M],len[M];
     27 bool vis[N],bh[M];
     28 int d[N];
     29 int n,m,cnt,ans,tmax,tmin,an;
     30 
     31 inline void add(int u,int v,int w)
     32 {
     33     to[cnt]=v; len[cnt]=w; next[cnt]=head[u]; head[u]=cnt++;
     34 }
     35 
     36 inline void read()
     37 {
     38     memset(head,-1,sizeof head); cnt=0;
     39     scanf("%d%d",&n,&m);
     40     for(int i=1,a,b;i<=m;i++)
     41     {
     42         scanf("%d%d",&a,&b);
     43         add(a,b,1); add(b,a,-1);
     44     }
     45 }
     46 
     47 inline int gcd(int x,int y)/*ans的初值可以设为0,gcd(0,100)=100,只要把0放在第一位*/
     48 {
     49     int ys;
     50     while(y)
     51     {
     52         ys=x%y;
     53         x=y; y=ys;
     54     }
     55     return x;
     56 }
     57 
     58 inline void dfs(int u)
     59 {
     60     vis[u]=true;
     61     for(int i=head[u];~i;i=next[i])
     62     {
     63         if(vis[to[i]])/*找到了环*/
     64         {/*计算环的长度并对每个环的长度求最大公约数:abs(d[u]+len[i]-d[to[i]]),这个式子很好理解d[u]+len[i]-d[to[i]],因为标记有可能是负值,所以要取绝对值*/
     65             ans=gcd(ans,abs(d[u]+len[i]-d[to[i]]));
     66         }
     67         else
     68         {
     69             d[to[i]]=d[u]+len[i];
     70             dfs(to[i]);
     71         }
     72     }
     73 }
     74 
     75 inline void tree(int u)
     76 {/*思路:将一个联通块找到第一个点标记为0,再用这个点找其他的点(不走重边),用所有点的编号的中的max-min+1,就是这个联通块中的结点数目*/
     77     vis[u]=true;
     78     tmax=max(tmax,d[u]);
     79     tmin=min(tmin,d[u]);
     80     for(int i=head[u];~i;i=next[i])
     81         if(!vis[to[i]])
     82         {
     83             bh[i]=bh[i^1]=true;
     84             d[to[i]]=d[u]+len[i];
     85             tree(to[i]);
     86         }
     87 }
     88 
     89 inline void go()
     90 {
     91     for(int i=1;i<=n;i++)
     92         if(!vis[i]) dfs(i);/*整张图有可能是森林*/
     93     if(ans)/*如果图中有环的话,那么ans就不是0*/
     94     {
     95         for(an=3;an<ans&&ans%an;an++);/*再用ans寻找大于3的(可能等于ans),且能整除ans的,也就是环的最小公约数,是最小*/
     96     }
     97     else/*没有环的情况*/
     98     {
     99         memset(vis,0,sizeof vis);
    100         for(int i=1;i<=n;i++)
    101             if(!vis[i])/*最大是每个联通块中的最长链的长度和,没找到一个!vis[i],就是一个联通块*/
    102             {
    103                 tmax=tmin=d[i]=0;
    104                 tree(i);
    105                 ans+=tmax-tmin+1;
    106             }
    107         an=3;/*最小就是3了*/
    108     }
    109     if(ans<3) ans=an=-1;/*注意要把这个ans小于3,放在最后面,因为可能在没有环的情况下,把各个联通块的最长路径加在一起也超不过3,比如那种极端的网状图,最长路径就有可能是1了,所以要把这个ans<3放在外面。*/
    110     /*我一开始就是仅仅把ans<3放到了有环的判断中,结果错了一个点*/ 
    111     printf("%d %d
    ",ans,an);
    112 }
    113 
    114 int main()
    115 {
    116     read();go();
    117     return 0;
    118 }

    我的代码:

      1 #define N 100010
      2 #define M 1000100
      3 #include<iostream>
      4 #include<cstring>
      5 #include<cmath>
      6 using namespace std;
      7 #include<cstdio>
      8 int n,m,ans=0,an,head[N],t=-1,d[N];
      9 bool vis[N]={0},bianflag[M<<1];
     10 struct Edge{
     11     int v,w,last;
     12 }edge[M<<1];
     13 int read1()
     14 {
     15     int ret=0,ff=1;
     16     char s=getchar();
     17     while(s<'0'||s>'9')
     18     {
     19         if(s=='-') ff=-1; 
     20         s=getchar();
     21     }
     22     while(s>='0'&&s<='9')
     23     {
     24         ret=ret*10+s-'0';
     25         s=getchar();
     26     }
     27     return ret*ff;
     28 }
     29 void add_edge(int u,int v,int w)
     30 {
     31     ++t;
     32     edge[t].v=v;
     33     edge[t].w=w;
     34     edge[t].last=head[u];
     35     head[u]=t;
     36 }
     37 void input()
     38 {
     39     n=read1();m=read1();
     40     int a,b;
     41     memset(head,-1,sizeof(head));
     42     for(int i=1;i<=m;++i)
     43     {
     44         scanf("%d%d",&a,&b);
     45         add_edge(a,b,1);
     46         add_edge(b,a,-1);
     47      } 
     48 }
     49 int gcd(int a,int b)
     50 {
     51     if(!b) return a;
     52     return gcd(b,a%b);
     53 }
     54 void dfs1(int k)
     55 {
     56     vis[k]=true;
     57     for(int l=head[k];l!=-1;l=edge[l].last)
     58     {
     59         if(vis[edge[l].v])
     60         {
     61             ans=gcd(ans,abs(d[k]+edge[l].w-d[edge[l].v]));
     62         }
     63         else {
     64             d[edge[l].v]=d[k]+edge[l].w;
     65             dfs1(edge[l].v);
     66         }
     67      } 
     68 }
     69 void dfs2(int k,int &maxx,int &minn)
     70 {
     71     vis[k]=true;
     72     maxx=max(maxx,d[k]);
     73     minn=min(minn,d[k]);
     74     for(int l=head[k];l!=-1;l=edge[l].last)
     75     {
     76         if(bianflag[l]) continue;
     77         bianflag[l]=true;
     78         bianflag[l^1]=true;
     79         d[edge[l].v]=d[k]+edge[l].w;
     80         dfs2(edge[l].v,maxx,minn);
     81     }
     82  } 
     83 int main()
     84 {
     85     input();
     86     for(int i=1;i<=n;++i)
     87     {
     88         if(!vis[i]) dfs1(i); 
     89     }
     90     if(ans)
     91     {
     92             for(an=3;an<ans&&ans%an;++an); 
     93         
     94      }
     95      else 
     96      {
     97          an=3;
     98          memset(vis,false,sizeof(vis));
     99         for(int i=1;i<=n;++i)
    100         {
    101             if(!vis[i])
    102             {
    103                 int maxx,minn;
    104                 maxx=minn=d[i]=0;
    105                 dfs2(i,maxx,minn);
    106                 ans+=maxx-minn+1;
    107                 //cout<<maxx<<" "<<minn<<" "<<ans<<endl; 
    108             }
    109          } 
    110      }
    111      if(ans<3)
    112     {
    113         ans=-1;an=-1; 
    114     }
    115      printf("%d %d",ans,an);
    116     return 0;
    117 }
    118  
  • 相关阅读:
    C# Linq to XML
    C# StopWatch
    C# 深拷贝代码
    基础练习 完美的代价
    基础练习 矩形面积交
    基础练习 矩阵乘法 时间限制:1.0s 内存限制:512.0MB
    阶乘计算 高精度
    杨辉三角形 递归与非递归
    数的分解 时间限制:1000 ms | 内存限制:65535 KB 难度:1
    数列排序 sort qsort
  • 原文地址:https://www.cnblogs.com/c1299401227/p/5861351.html
Copyright © 2020-2023  润新知