Title: [NOI2001]食物链
题目描述
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是“1 X Y”,表示X和Y是同类。
第二种说法是“2 X Y”,表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。
当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1.当前的话与前面的某些真的话冲突,就是假话;
2.当前的话中X或Y比N大,就是假话;
3.当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1≤N≤50,000)和K句话(0≤K≤100,000),输出假话的总数。
输入
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 若D=1,则表示X和Y是同类。 若D=2,则表示X吃Y。
输出
只有一个整数,表示假话的数目
样例输入
Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
样例输出
3
看看这道题吧。这道题我们可以把出现的每个动物看成一个点,建立一个图。这个图的定义如下:
若x与y是同类,则连一条边x->y值为0;
若x吃y,则连一条边x->y值为1;
若x被y吃,则连一条边x->y值为2;
如何判断在同一个联通集合中的x、y的类别?我们把x到根节点的距离mod 3设为wt[x],y到根节点的距离mod 3设为wt[y]。所以:
当wt[y]-wt[x]≡0 mod 3时,x、y同类;
当wt[y]-wt[x]≡1 mod 3时,x吃y;
否则,y吃x;(画一个图想一想...)
若x、y不在同一个集合内,我们就要把x、y的集合联通起来,即在两个根节点之间连一条线。
设x的根节点为r1,y的根节点为r2,r1->r2的值为d。
我们把连接后的集合根节点设为r2,即把r1接到r2后面。
∴wt[x]+=d
∴wt[y]-wt[x]-d≡1/0 mod 3 (1:x吃y;0:x与y是同类)“/”表示两种情况:“或”
∴d≡wt[y]-wt[x]-1/0 mod 3
∴d=((wt[y]-wt[x]-1/0)%3+3)%3
所以代码就出来了:
#include<cstdio>
#include<cstring>
int n,k,ans;
int f[50001];
int wt[50001];
int root(int x)//路径压缩带权并查集
{
if(f[x]==-1) return x;
int d=f[x];
f[x]=root(f[x]);
wt[x]+=wt[d];
wt[x]%=3;//迟早要模3,就先模掉
return f[x];
}
int main()
{int a,b,c;
memset(f,-1,sizeof f);
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(b>n||c>n||(a==2&&b==c)){ans++;continue;}
int r1=root(b);
int r2=root(c);
if(r1==r2)
{
if((wt[c]-wt[b])%3!=0&&a==1) ans++;
else if(((wt[c]-wt[b])%3+3)%3!=1&&a==2) ans++;
}
else
{
if(a==2){f[r1]=r2;wt[r1]=((wt[c]-wt[b])%3-1+3)%3;}
else{f[r1]=r2;wt[r1]=((wt[c]-wt[b])%3+3)%3;}
}
}
printf("%d",ans);
}