并查集
一.普通并查集
int find(int x){ if(f[x]==x)return x; return f[x]=find(f[x]); }
二.带权并查集
int find(int x){ if(f[x]==x)return x; find(f[x]); work();//用儿子与爷爷关系和(儿子与父亲关系和父亲与爷爷关系)的关联,更新权值为儿子与爷爷关系 return f[x]=f[f[x]]; }
http://cogs.pro/cogs/problem/problem.php?pid=298
/****************************************************************** 带权并查集 Part I - 权值(relation)的确定。 我们可以用动物之间“相对”的关系来确定一个并查集。 0 - 这个节点与它的父节点是同类 1 - 这个节点被它的父节点吃 2 - 这个节点吃它的父节点。 第一个数字(下文中,设为d)指定了后面两种动物的关系: 1 - X与Y同类 2 - X吃Y Part II - 路径压缩,以及节点间关系确定 (1)路径压缩节点时的算法 通过穷举我们可以发现 f[now]=f[f[now]] relation[now]=(relation[now]+relation[f[now]]) % 3 推理过程 父亲是自己 relation[now]=0;0+0=0; 父亲不是自己 爷爷(可能还是父亲)和 父亲和儿子 儿子与爷爷 0 0 (i + j)%3 = 0 0 1 (i + j)%3 = 1 0 2 (i + j)%3 = 2 1 0 (i + j)%3 = 1 1 1 (i + j)%3 = 2 1 2 (i + j)%3 = 0 2 0 (i + j)%3 = 2 2 1 (i + j)%3 = 0 2 2 (i + j)%3 = 1 注:要先更新父亲再更新儿子,可以直接跳到根节点 (2)集合间关系的确定 relation[find(y)]=(relation[x]-relation[y]+(d-1)+3) % 3; 证明: y与find(x)的关系为w=(relation[x]+(d-1))%3(其中(d-1)为y与x的关系,可以把x当作y的父节点推出来) find(y)与find(x)的关系要用路径压缩算法反着推出来 relation[find(y)]=(w-relation[y]+3)%3 Part III - 判断 如果 find(x)==find(y)代表以前确立了关系,(d-1)表示y与x的关系 relation[y]是y与根关系,(3-relation[x])%3是根与x关系 如果(relation[y]-relation[x]+3)%3!=d-1则为假话 ******************************************************************/ #include<cstdio> #define maxn 50000 int f[maxn],relation[maxn]; int find(int x){ if(f[x]==x)return x; find(f[x]); relation[x]+=relation[f[x]];relation[x]%=3; return f[x]=f[f[x]]; } int mo(int x){ return x>=0?x%3:(x%3)+3; } int main(){ freopen("eat.in","r",stdin); freopen("eat.out","w",stdout); int n,ans=0,k,d,x,y,a,b; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ f[i]=i; } for(int i=0;i<k;i++){ scanf("%d%d%d",&d,&x,&y);d--; if(x>n||y>n){ ans++; continue; } a=find(x),b=find(y); if(a==b){ if(mo(relation[y]-relation[x])!=d)ans++; } else{ relation[b]=mo(relation[x]-relation[y]+d); f[b]=a; } } printf("%d",ans); return 0; }