https://www.acwing.com/problem/content/242/
这道题看起来相当有难度,这道题不仅仅是一个并查集。更类似于一个带权并查集。
我们在这里引用了p[N],d[N].d是我们的权,指的是这个点到根节点的距离。然后根据y总讲的,距离根节点的距离模3的一些特殊性质(因为y总把它放在了一个树上),
1.模3余1时x吃y
2.模3余2时y吃x
3.模3余0时x,y同类。
在这里可以清楚的看到,模3余0的点离根节点最远的,是可以吃那个上一个点的,根节点也可以吃这个点,所以这两个点是一类。然后我们很明显我们要创建这个根节点为某一个值的树,我们用并查集实现这个功能,至于一些细节在代码中说清。
#include<iostream>
using namespace std;
const int N=1e5+10;
int p[N],d[N];
int find(int x)
{
if(p[x]!=x)
{
int u=find(p[x]);
d[x]+=d[p[x]];//原来的d[x]是x到p[x]的距离,加上父节点到根节点的距离,就是x到根节点的距离
//为什么原来的d[x]是x到p[x]的距离,看下面标注的**
p[x]=u;
}
return p[x];
}
int main(void)
{
int n,m,res=0;
cin>>n>>m;
for(int i=1;i<=n;i++) p[i]=i;
for(int i=1;i<=m;i++)
{
int t,x,y;
cin>>t>>x>>y;
int px=find(x),py=find(y);
if(x>n||y>n) res++;
else if(t==1)
{
if(px==py&&(d[x]-d[y])%3) res++;
else if(px!=py)
{
p[px]=py;
d[px]=d[y]-d[x];//**这两处的标注都表明了这得到的是两个原来的父节点距离的差
//这个意思是,y为根节点,x的祖宗到这个根节点的距离等于y到根节点的距离-x到祖宗的距离
//这样一定能保证(d[x]-d[y])%3==0
//因为(d[x](d[x]+d[px])-d[y])%3==0
}
}
else if(t==2)
{
if(px==py&&(d[x]-d[y]-1)%3) res++;
else if(px!=py)
{
p[px]=py;
d[px]=d[y]-d[x]+1;//**
//同理,这样一定能保证(d[x]-d[y])%3==1
//因为(d[x](d[x]+d[px])-d[y])%3==1
}
}
}
cout<<res;
}