带权并查集。
学习参考: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; }