[NOI 2008]假面舞会
题目
一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择一个自己喜欢的面具。
每个面具都有一个编号,主办方会把此编号告诉拿该面具的人。为了使舞会更有神秘感,主办方把面具分为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
INPUT1
6 5
1 2
2 3
3 4
4 1
3 5OUTPUT1
4 4
INPUT2
3 3
1 2
2 1
2 3OUTPUT2
-1 -1
解题报告
当时考试的时候,一看这种带顺序的类似图论的奇怪东西,就想到了拓扑排序
然后......
并不会打拓扑啊QAQ,于是打了个奇(chun)奇(dao)怪(bao)怪(zha)的dfs,好像拿了个特判的-1 -1的分QAQ
正解仍然是dfs
由题目可知,从一个点连出去的点标号一样,几个点指向同一个点,标号也肯定一样。而且,有一句很重要的话:
戴第k 类面具的人能看到戴第1 类面具的人的编号。
那么如果我们找到了环,并将所有环的GCD找出来,我们就找到了最大值。正确性显然= =
如果没有环呢?
显然我们还可以找到链,而链则不会受到上面那个条件的制约,我们就可以很轻松的解决这个问题啦。
然而我们似乎忘记了一个很神奇的东西= =
当我们遇到有各种方向边的环时,显然通过上面的证明,会有第一个点有多个不同标号的情况,这是不可能的,所以,只能看作是不停的由第k个点看第一个点的特殊情况。
如何处理?
很简单,设某个方向边组成的那半个环为ax+k=b的循环,另一个方向边组成的那半个环为cx+k=b的循环。(这里的循环指从第一个点一直走到第k个点再走回第一个点)。显然环是要一样大的,而多出n个环数的点数也应是一样大的(不然起点或终点就会有不止一个标号)。
所以,我们只需建双向边,正边权值1,反边权值-1,最后取个绝对值,就可当正常环看待了(正确性显然= =)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 inline int read(){ 6 int sum(0); 7 char ch(getchar()); 8 while(ch<'0'||ch>'9') 9 ch=getchar(); 10 while(ch>='0'&&ch<='9'){ 11 sum=sum*10+ch-'0'; 12 ch=getchar(); 13 } 14 return sum; 15 } 16 struct edge{ 17 int s,e,n,w; 18 }a[2000001]; 19 int pre[100001],tot; 20 inline void insert(int s,int e,int w){ 21 a[++tot].s=s; 22 a[tot].e=e; 23 a[tot].w=w; 24 a[tot].n=pre[s]; 25 pre[s]=tot; 26 } 27 inline void swp(int &a,int &b){ 28 a^=b; 29 b^=a; 30 a^=b; 31 } 32 /*inline int gcd(int a,int b){ 33 //cout<<a<<' '<<b<<endl; 34 if(a==0) 35 return b; 36 if(b==0) 37 return a; 38 int i,j; 39 for(i=0;0==(a&1);i++) 40 a>>=1; 41 for(j=0;0==(b&1);j++) 42 b>>=1; 43 if(j<i) 44 i=j; 45 while(1){ 46 if(a<b) 47 swp(a,b); 48 if(0==(a-=b)) 49 return b<<i; 50 while(a&1) 51 a>>=1; 52 } 53 }*/ 54 inline int gcd(int a,int b){ 55 if(a==0) 56 return b; 57 if(b==0) 58 return a; 59 return a%b?gcd(b,a%b):b; 60 } 61 inline int jdz(int x){ 62 return x>=0?x:-x; 63 } 64 inline int my_min(int a,int b){ 65 return a<b?a:b; 66 } 67 inline int my_max(int a,int b){ 68 return a>b?a:b; 69 } 70 int n,m; 71 bool vis[100001]={0}; 72 int f[100001]={0}; 73 int mx,mn; 74 int circle(0),ans(0); 75 inline void dfs(int u,int cnt){ 76 if(vis[u]) 77 return; 78 f[u]=cnt;vis[u]=1; 79 mx=my_max(mx,cnt); 80 mn=my_min(mn,cnt); 81 for(int i=pre[u];i!=-1;i=a[i].n){ 82 int e(a[i].e); 83 if(vis[e]){ 84 int cir(jdz(cnt-f[e]+a[i].w)); 85 if(cir){ 86 if(circle==0) 87 circle=cir; 88 else 89 circle=gcd(circle,cir); 90 } 91 } 92 else 93 dfs(e,cnt+a[i].w); 94 } 95 } 96 int main(){ 97 // freopen("party2008.in","r",stdin); 98 // freopen("party2008.out","w",stdout); 99 memset(pre,-1,sizeof(pre)); 100 n=read(),m=read(); 101 for(int i=1;i<=m;i++){ 102 int x(read()),y(read()); 103 insert(x,y,1); 104 insert(y,x,-1); 105 } 106 for(int i=1;i<=n;i++) 107 if(!vis[i]){ 108 mx=-0x7fffffff; 109 mn=0x7fffffff; 110 dfs(i,0); 111 ans+=mx-mn+1; 112 } 113 if(circle==0){ 114 if(ans<3){ 115 printf("-1 -1"); 116 return 0; 117 } 118 else{ 119 printf("%d %d",ans,3); 120 return 0; 121 } 122 } 123 else{ 124 if(circle<3){ 125 printf("-1 -1"); 126 return 0; 127 } 128 else{ 129 printf("%d ",circle); 130 int gg(0); 131 for(int i=3;i<=circle;i++){ 132 if(circle%i==0){ 133 gg=i; 134 break; 135 } 136 } 137 if(gg==0) 138 printf("%d",circle); 139 else 140 printf("%d",gg); 141 } 142 } 143 }
ps:一直在WA,最后发现是GCD打错了= =