动物王国中有三类动物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),输出假话的总数。
参考连接:点击打开链接
集合划分:
注意,这里不是根据x与p[x]是否是同类来划分。而是根据“x与p[x]能否确定两者之间关系”来划分,若能确定x与p[x]关系,则它们同属一个集合
p[x]表示x根结点。r[x]表示p[x]与x关系。r[x]=0 表示p[x]与x同类;1表示p[x]吃x;2表示x吃p[x]。
- 当 d = 1的时候,( d - 1 ) = 0,也就是我们制定的意义
- 当 d = 2的时候,( d - 1 ) = 1,代表Y被X吃,也是我们指定的意义。
- 所以,这个0,1,2不是随便选的
路径压缩时的节点算法:r[x]=(r[x]+r[pre[x]])%3;
穷举证明:
i j
爷爷 父亲 儿子 儿子与爷爷
0 0 (i + j)%3 = 0
0 1 (i + j)%3 = 1
0 2 (i + j)%3 = 2
1 0 (i + j)%3 = 1
1 1 (i + j)%3 = 2
1 2 (i + j)%3 = 0
2 0 (i + j)%3 = 2
2 1 (i + j)%3 = 0
2 2 (i + j)%3 = 1
嗯,这样可以看到,( 儿子relation + 父亲relation ) % 3 = 儿子对爷爷的relation
这就是路径压缩的节点算法
将x和y所在集合连接起来:
这个公式,是分三部分,这么推出来的:
( d - 1 ) :这是X和Y之间的relation,X是Y的父节点时,Y的relation就是这个
3 - relation[y] = 根据Y与根节点的关系,逆推根节点与Y的关系
这部分也是穷举法推出来的,我们举例:
0(父子同类)( 3 - 0 ) % 3 = 0
1(父吃子) ( 3 - 1 ) % 3 = 2 //父吃子
2(子吃父) ( 3 - 2 ) % 3 = 1 //子吃父
判断:
先处理特殊情况:
1.当x>n或y>n时,为假话(在这里竟然wa了一次...囧还是太不认真了)
2.当d=2而x=y时,为假话
其实所有的不同集合到最后都会被合并成一个集合的。我们只要在一个集合中找那些假话就可以了。
(1)首先,如何判断
1 X Y是不是假话。//此时 d = 1
if ( X 和 Y 不在同一集合) Union(x,y,xroot,yroot,d)
else if x.relation != y.relation ->假话
(2)其次,如何判断
2 X Y是不是假话 //此时d = 2
if ( X 和 Y 不在同一集合)Union(x,y,xroot,yroot,d)
else (relation [y]+ 3 - relation[x] ) % 3 != 1 ->假话
这个公式是这么来的:
3 - relation[x]得到了根节点关于x的relation,
relation [y]+ 3 - relation[x]得到了y关于x的relation
所以,只要y关于x的relation不是1,就是y不被x吃的话,这句话肯定是假话!
综合(1) 和(2),无论d=1或2,只要满足 ((relation[y]-relation[x]+3) Mod 3)<>(d-1) 即为假话
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<map>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int pre[50010];
int r[50010];
int findset(int x)
{
if(pre[x]!=x)
{
int fy=findset(pre[x]);
r[x]=(r[x]+r[pre[x]])%3;//路径最多为3层
pre[x]=fy;
}
return pre[x];
}
bool union_set(int d,int x,int y)
{
int fx=findset(x);
int fy=findset(y);
if(fx==fy)
{
if((3-r[x]+r[y])%3!=d-1)
return 1;
else
return 0;
}
pre[fy]=fx;
r[fy]=(r[x]+d-1+3-r[y])%3;
return 0;
}
int main()
{
int n,k;
scanf("%d%d",&n,&k);
int ans=0;
for(int i=1;i<=n;i++)
pre[i]=i;
memset(r,0,sizeof(r));
for(int t=1;t<=k;t++)
{
int d,x,y;
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n)
ans++;
else if(d==2&&x==y)
ans++;
else
{
if(union_set(d,x,y))
ans++;
}
}
cout<<ans<<endl;
return 0;
}