http://acm.pku.edu.cn/JudgeOnline/problem?id=1182
#include <stdio.h> #define max 50000+5 int f[max]; int r[max]; /* rank[x]表示father[x]与x的关系 rank[x] == 0 表示father[x]与x是同类 rank[x] == 1 表示x吃father[x] rank[x] == 2 表示father[x]吃x */ void make_set(int x) { f[x]=x; r[x]=0; } int find_set(int x) { if (f[x]==x) return x; int t=f[x]; f[x]=find_set(t); r[x]=(r[t]+r[x])%3; //important. 更新r[]数组中x与代表元素的相对关系。更新原因: //代表元素在union_set操作中被改变了。 //至于这个式子的推得.可以枚举rx与p[x], p[x]与x的关系,然后观察得到。 //更好的方法是向量运算。 return f[x]; } void union_set(int x,int y,int d) { int a=find_set(x); int b=find_set(y); f[a]=b; r[a] = (r[y] - r[x]+ 2 + d ) % 3; //同上。这两个关系的推得实际上是这道题的关键所在。 } int main() { int n,k; int d,x,y,lie=0; scanf("%d%d",&n,&k); for (int i=0;i<max;i++) make_set(i); while (k--) { int a,b; scanf("%d%d%d",&d,&x,&y); if (d==2&&x==y) {lie++;continue;} if (x>n||y>n) {lie++;continue;} a=find_set(x); b=find_set(y); if (a==b) { if (d==1&&r[x]!=r[y]) lie++; else { if (d==2&&r[x]!=(r[y]+1)%3) lie++; } } else union_set(x,y,d); } printf("%d/n",lie); return 0; }
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3
我在这里演示下这个例子
n=100;k=7;
lie=0;
>>d x y
>>1 101 1 x>n lie++;
>>2 1 2 f[1]==1与f[2]==2都是指向自己,接着union_set(1,2,2)将1,2合并
我们进入union_set看看,f[1]=2;r[1]=(r[2]-r[1]+2+2)%3=(0-0+4)%3=1,
即r[1]=1,1吃2(f[1])
>>2 2 3 同上,得到f[2]=3,r[2]=(r[3]-r[2]+2+2)%3=1,2吃3(f[2])
f[3]=3,r[3]=0
>>2 3 3 d==2,x==1,y==3 lie++
>>1 1 3 d=1,x=1,y=3;
我们进入find_set看看,find_set(1) {f[1]==2!=1,t=2,f[1]=find_set(t)
{f[2]==3!=2,t=3,f[2]=find_set(t){f[3]==3,return f[3]即3},f[2]=3
r[2]=(r[t]+r[2])%3=1,return f[2]即3},f[1]=3,r[1]=(r[t]+r[1])%3= 2,return 3(f[1]).即f[1]=3,r[1]=2
d==1,r[1]==2,r[3]==0;r[1]!=r[3]即1与3对各自的上节点不具有相同行为
所以1与3不属于同类 lie++
>>2 3 1 d==2,x==3,y==1
f[3]==3而f[1]==3,所以可以进行真假话的判断
d==2,r[3]==0,r[1]==2;r[3]==(r[1]+1)%3(即3吃1)
>>1 5 5 d==1,x==5,y==5
f[x]==5,f[y]==5,可以进行真假话的判断
d==1,r[x]==5,r[y]==5