这个比我还年长的题用所谓的带权并查集。
首先,在并查集入门题《亲戚》中我们用并查集描述两人的关系。
所以,我们也可以用并查集来表示动物间食物链的关系。
但是吃与被吃这种关系一个数组是开不完的。
因此,我们得把并查集这个森林中的每颗树的树根利用起来。
若x,y是同类,所以我们从x到y连一条权值为0的有向边。(其实并查集只能连有向边)
若x吃y,那么我们就从x到y连一条权值为1的有向边。
这个假设方法十分方便,因为你可以直接从x到y连一条权值为(d-1)的有向边。
但实际上并不是这样的。
因为两个并查集的合并是将一棵树的根连在另一个根上(废话)。
如果x,y不在同一颗树中:
我们该从x的根向y的根连权值为n的边;
其次,我要说,所有的树边的权值都不用存,存儿子到根的长度dis[ i ],然后在并查集路径压缩(自行百度)时更改就行。
因为x与y的关系是(d-1),x与y的距离是(d-1)
x树连在y树后dis[x]应该为dis[x]+n.
所以(dis[x]-dis[y]+n)== d-1
等等,因为关系只有三种:吃,被吃,同类。
所以所有的值都可以(必须)%3(注意负数要处理成正数),毕竟与根距离为0的 和 与根距离为3的动物是同类。。
所以你可以算出n。
然而在我们津津有味的加边,路径压缩后,我们会发现:如果x,y已经在一个集中,也就是意味着我们不可以在他们之间加边,那就判断这句话对不对就行了。。。。。。如果想到这,就很简单了。
AC代码:
#include<cstdio>
#include<cctype>
using namespace std;
int n,k,x,y,d,_x,_y,f[50001],dis[50001],ans;
int find(int x){
if(x==f[x]) return x;
int tmp=f[x];
f[x]=find(f[x]);
dis[x]=(dis[x]+dis[tmp])%3;
return f[x];
}
void getint(int &t){
char c;
t=0;
do{c=getchar();}while(!isdigit(c));
while(isdigit(c)){
t=t*10+c-'0';c=getchar();
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=k;i++){
getint(d);getint(x);getint(y);
if(x>n||y>n){ans++;continue;}
_x=find(x);
_y=find(y);
if(_x!=_y){
f[_x]=_y;
dis[_x]=(dis[y]-dis[x]+d-1+3)%3;
}
else
if(((dis[x]-dis[y])%3+3)%3 != d-1)
ans++;
}
printf("%d",ans);
}