您的位置:首页 > 其它

UVA 11987 - Almost Union-Find 并查集的活用 id化查找

2013-08-10 15:33 447 查看
受教了,感谢玉斌大神的博客。

这道题最难的地方就是操作2,将一个集合中的一个点单独移到另一个集合,因为并查集的性质,如果该点本身作为root节点的话,怎么保证其他点不受影响。

玉斌大神的思路很厉害,受教受教,即,由于题目最终输出集合的元素个数与权值总和,故添加一个delete操作,将该点(设为P)所在集合的rank和sum值减小,将p的father引向一个从没定义过的点,(可以设置为(总数++)点),这样,虽然看似P还留在原集合,但仅仅作为一个空骨架,并不对集合的rank和sum产生影响。

具体实现,需要借助一个辅助数组 id[], id[]初始和father[]相同,但一旦需要删除操作,即将id[p]=++n,指向一个新位置,下次father[id[x]]即指向了新位置,跟原集合无关了。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int father[200005]; //由于最坏可能有1000000次删除操作,故最大的数组量
int rank[200005];
long long sum[200005];
int id[200005];
int n;
int cnt;
void init()
{
for(int i=1;i<=n;i++)
{
father[i]=i;
rank[i]=1;
sum[i]=i;
id[i]=i;
}
cnt=n;
}
int findset(int x)
{
if (x!=father[x])
{
father[x]=findset(father[x]);
}
return father[x];
}
void unionset(int x,int y)
{
x=id[x];
y=id[y]; //所有点的指向,全部通过一个间接的id[x]的值来指向相应的值 即将所有点全部id化,不再已原来的本身值为id。
int r1=findset(x);
int r2=findset(y);
if (r1==r2) return;
if (rank[r1]>rank[r2])
{
father[r2]=r1;
rank[r1]+=rank[r2];
sum[r1]+=sum[r2];
}
else
{
father[r1]=r2;
rank[r2]+=rank[r1];
sum[r2]+=sum[r1];
}
}
void q1()
{
int p,q;
scanf("%d %d",&p,&q);
unionset(p,q);
}
void dele(int x)
{
int r=findset(id[x]);
rank[r]--;
sum[r]-=x;
id[x]=++cnt;
father[id[x]]=id[x];
rank[id[x]]=1;
sum[id[x]]=x;
}
void q2()
{
int p,q;
scanf("%d %d",&p,&q);
int    r1=findset(id[p]);
int    r2=findset(id[q]);
if (r1==r2) return;
dele(p);
unionset(p,q);
}
void q3()
{
int p;
scanf("%d",&p);
int root=findset(id[p]);
printf("%d %lld\n",rank[root],sum[root]);
}
int main()
{
int q;
while (scanf("%d %d",&n,&q)!=EOF)
{
init();
int i,j,k;
for (i=1;i<=q;i++)
{
int deno;
scanf("%d",&deno);
if (deno==1) q1();
if (deno==2) q2();
if (deno==3) q3();
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: