[NOI2001] 食物链
带权并查集。
学习参考:https://agatelee.cn/2017/05/%E5%B8%A6%E6%9D%83%E5%B9%B6%E6%9F%A5%E9%9B%86/
注意事项与可能产生的疑惑。
1:节点的权直接代表与根节点的关系。
2:路径压缩中可能出现节点权并不代表与当前根节点的关系,因为该节点是新合并的。
且从某个节点到根节点的路径中,至多有一个点会存在这样的关系。
且这个点是之前没有路径压缩过的点。
该点的权代表与之前跟节点的关系,且该节点之前的根节点一定是f[x],即该节点的父节点。
那么 w[x] 应为 ( w[x] + w[f[x]] ) % 3。
// q.c
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int M=50000+10;
/****************************************************************/
int f[M],w[M];
int finds(int x) {
if(f[x]!=x) {
int tmp=f[x];
f[x]=finds(f[x]);
w[x]=(w[tmp]+w[x])%3; // 所有未路径压缩过的点的w[]值是与先前根节点的关系.
}
return f[x];
}
/****************************************************************/
int main() {
freopen("eat.in","r",stdin);
freopen("eat.out","w",stdout);
int n,q,opt,x,y,fx,fy,ans=0;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) {
f[i]=i,w[i]=0;
}
for(int i=1;i<=q;i++) {
scanf("%d%d%d",&opt,&x,&y);
if(x>n||y>n) {
ans++; continue;
}
if(opt==1) {
fx=finds(x);
fy=finds(y);
if(fx==fy) {
if(w[x]!=w[y]) ans++;
continue;
}
f[fx]=fy;
w[fx]=(w[y]-w[x]+3)%3; // w[x]+w[fx]=w[y].
} else {
fx=finds(x);
fy=finds(y);
if(fx==fy) {
if(w[x]!=(w[y]+1)%3) ans++;
continue;
}
f[fx]=fy;
w[fx]=(w[y]-w[x]+4)%3; // w[x]+w[fx]=w[y]+1.
}
}
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号