UVa-11987 Almost union-find(带删除操作的并查集)

来源:互联网 发布:warframe防火墙端口 编辑:程序博客网 时间:2024/05/18 02:32

题意:有三种操作:

1 p q:合并元素p和q所在集合

2 p q:把元素p移动到q所在集合

3 p   :输出p所在集合元素个数和该集合所有元素之和

分析:这道题考察了并查集的删除操作以及统计并查集元素和, 对于每一个集合,我们只需要考虑根节点root号位置,注意find查找到的是根节点的位置,实际根节点有可能不在这里,用cnt[root]和sum[root]来代表该集合元素个数与元素之和,并且在每一次操作时进行更新,初始时k号节点就在k号位置

下面重点讲讲删除操作,刚开始我的写法是像链表那样,把i的子节点与i的父节点相连,但是这样会超时。

我们用id[i]来代表节点i实际的位置,意思就是比如id[2]=2说明节点2在2号位置,id[2]=6说明节点2在6号位置,每次删除节点,我们可以新建一个比n大的节点序号tot,然后使id[i]=tot, 再合并id[i]和另一个集合。比如n=5,tot=6,i=2,id[i]=6  这样下一次我们再找2号的根节点时,我们找的是6号的根节点,也就是2号所属的当前集合的根节点。

这种做法无论删除的店是不是根节点都是成立的

#include<iostream>#include<cstdio>#include<cstring>#define MAXN 100005using namespace std;int pa[MAXN];long long cnt[MAXN], sum[MAXN];//分别代表集合元素个数和元素总和 int id[MAXN];//id[i]代表i现在所处的位置,i没被删除时id[i]=i,i被删除移动后id[i]=tot>n int n, m;int k, p, q;int find(int x){ return pa[x]==x? pa[x]=x:find(pa[x]);}int main(){while(~scanf("%d%d", &n, &m)){int tot = n;for(int i = 1; i<=n; i++){pa[i] = i;cnt[i] = 1;sum[i] = i;id[i] = i;}for(int i = 0; i<m; i++){scanf("%d", &k);if(k==1){scanf("%d%d", &p, &q);int root1 = find(id[p]), root2 = find(id[q]);//要找当前的位置,所以是idif(root1==root2) continue;pa[root1] = root2;sum[root2]+=sum[root1];cnt[root2]+=cnt[root1];}if(k==2){scanf("%d%d", &p, &q);int root1 = find(id[p]), root2 = find(id[q]);if(root1==root2) continue;id[p] = ++tot;//创建一个新节点,并且用新节点的值作为结点p的新位置pa[id[p]] = pa[id[q]];sum[root2]+=p;cnt[root2]++;sum[root1]-=p;cnt[root1]--;}if(k==3){scanf("%d",&p);int root = find(id[p]);printf("%lld %lld\n", cnt[root], sum[root]);}}}return 0;}

阅读全文
0 0