种类并查集(POJ 1703)
1703 -- Find them, Catch them
http://poj.org/problem?id=1703
题目大意:有2个敌对帮派,输入D a b表示a,b在不同帮派,输入A a b表示询问a,b是否是在一个帮派。
题解:因为并查集中的元素均是有联系的,否则也不会被合并到当前集合中。那么我们就把这2个元素之间的关系量转化为一个偏移量,
假设
x->y 偏移量0时 x和y同帮派
x->y 偏移量1时 x和y不同帮派
不妨继续假设,x的当前集合根节点tx,y的当前集合根节点ty。
如果tx和ty不相同,那么我们把ty合并到tx上,并且更新deltx[ty]值(注意:deltx[i]表示i的当前集合根节点到i的偏移量!!!!)
此时 tx->ty = tx->x + x->y + y->ty,可能这一步就是所谓向量思维模式吧
上式进一步转化为:tx->ty = (deltx[x]+1-deltx[y])%2 = deltx[ty],(模2是保证偏移量取值始终在[0,1]间)
接下来把这个想法再运用到路径压缩中:
ffx
| \
fx \
| /
| /
x
路径压缩过程中会将fx的父亲变为x的父亲,所以要改变相对关系,即偏移量。
ffx->fx+fx->x=ffx->x;
转换成:delta[x]=(delta[fx]+delta[x])%2;
即
int find(int x)
{
if(x==fa[x])
return fa[x];
int f=fa[x];
fa[x]=find(fa[x]);
delta[x]=(delta[x]+delta[f])&1; //这句和上句不能交换位置,delta[]的改变是随着路径压缩是fa[]的改变而改变
return fa[x];
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> int n; int fa[100001],delta[100001]; //deltx[i]表示i的当前集合根节点到i的偏移量 void init() { int i; for(i=1;i<=n;i++) { fa[i]=i; delta[i]=0; } } int find(int x) { if(x==fa[x]) return fa[x]; int f=fa[x]; fa[x]=find(fa[x]); delta[x]=(delta[x]+delta[f])&1; //这句和上句不能交换位置,delta[]的改变是随着路径压缩是fa[]的改变而改变 return fa[x]; } void merge(int x,int y) { int fx=find(x); int fy=find(y); if(fx!=fy) { fa[fy]=fx; delta[fy]=(delta[x]+1-delta[y])&1; } } int main() { int t,m,i,a,b; char ch; scanf("%d",&t); while(t--) { scanf("%d%d%*c",&n,&m); //%*c替掉回车,ch吃回车,经常搞错,哎。。。 init(); for(i=1;i<=m;i++) { scanf("%c%d%d%*c",&ch,&a,&b); if(ch=='D') merge(a,b); if(ch=='A') { if(find(a)==find(b)) { if(delta[a]==delta[b]) //根节点到a和b的偏移量相同时,a和b必定为同一帮派。朋友的朋友是朋友,敌人的敌人也是朋友 printf("In the same gang.\n"); else printf("In different gangs.\n"); } else printf("Not sure yet.\n"); } } } return 0; }