并查集
p(x)为x所属树的根节点。kind(x)为x与该树根节点的关系。height(x)为树x的高度。
现在规定:
0:同类关系。
1:吃关系。
2:被吃关系。
我们用x--r-->y表示x和y之间的关系是r,比如x--1--y代表x吃y。现在,若已知x--r1-->y,y--r2-->z,如何求x--?-->z?,于是我们不难发现,x--(r1+r2)%3-->z。有了这个结论,我们就可以做题了。
我们初始化kind(1-N)=0,表示自己和自己为同类。P(1-N)=i,代表各个节点都是一棵树。当D X Y时,则应合并X的根节点和Y的根节点,同时修改各自的kind。那么问题来了,合并了之后,被合并的根节点的kind值如何变化呢?
现有x和y,d为x和y的关系,px和py分别是x和y的根节点,于是我们有x--kind[x]-->px,y--kind[y]-->py,显然我们可以得到px--(3-kind[x])-->x,py--(3-kind[y])-->y。假如合并后px为新的树的根节点,那么原先px树上的节点不需变化,py树则需改变了,因为kind值为该节点和树根的关系。这里只改变kind(py)即可,因为在进行find_set操作时可相应改变py树的所有节点的kind值。于是问题变成了py--?-->px。我们不难发现py--(3-kind[y])-->y--(3-d)-->x--kind[x]-->px,根据前面的结论,我们有py--(3-kind[y]+3-d+kind[x])%3-->px。我们求解了px和py的关系了。
#include <stdio.h>
int father[50001];
int kind[50001];
int height[50001];
int Find(int x)
{
if(father[x] == x) return x;
int t;
t = father[x];
father[x] = Find(father[x]);
kind[x] = (kind[x] + kind[t]) % 3;
return father[x];
}
void Union(int px,int py,int x,int y,int d)
{
if( height[px] < height[py] )
{
father[px] = py;
kind[px] = (3-kind[x]+d+kind[y]) % 3 ;
}
else
{
if( height[px] == height[py] )
height[px]++;
father[py] = px;
kind[py] = ( 3 - kind[y] + 3 - d + kind[x]) % 3;
}
}
int main()
{
int i,sum,n,t,d,x,y,px,py;
scanf("%d%d",&n,&t);
for( i = 1 ; i <= n ; i++)
{
father[i] = i;
kind[i] = height[i] = 0;
}
sum = 0 ;
while(t--)
{
scanf("%d%d%d",&d,&x,&y);
if( x > n || y > n ) {sum++;continue;}
if( d==2 && x==y ) {sum++;continue;}
px = Find(x);
py = Find(y);
if( px == py )
{
if( d==1 && kind[x] != kind[y] ) {sum++;continue;}
if(d==2&&(kind[x]+3-kind[y])%3!=1)
//else if( d==2 && !(kind[x]==1&&kind[y]==0) && !(kind[x]==0&&kind[y]==2) && !(kind[x]==2&&kind[y]==1) )
{sum++;continue;}
}
else
Union(px,py,x,y,d-1);
}
printf("%d\n",sum);
return 0;
}
浙公网安备 33010602011771号