您的位置:首页 > 其它

uva 11987 Almost Union-Find

2017-12-15 11:18 465 查看
题目:Almost Union-Find

思路:并查集。

对于操作  2 x y ,此时既要把x移动到另一个集合,又要保证x下挂着的数能连接到根节点。所以可以把数x复制一份到另一个集合,原集合中的x的数值、数目都不存在,但是仍然指向根。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <deque>
#include <set>
#include <cstring>
#include <map>
using namespace std;

#define ll long long

int n,m;
int fa[200005];
ll s[200005]; //以i为根的所有数的和
int num[200005]; //以i为根的数的数量
int a[200005]; //一个数x的有效编号为a[x]

int find(int x) {
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}

void Union(int x,int y) { //合并x,y所在的集合
int fa1=find(x),fa2=find(y);
if(fa1==fa2) return ;
fa[fa1]=fa2;
num[fa2]+=num[fa1];
s[fa2]+=s[fa1];
}

void Remove(int x,int y) { //将x移动到y所在的集合
int fa1=find(a[x]),fa2=find(a[y]);
if(fa1==fa2) return ;
num[fa1]--; //从集合1中去掉数x的信息
s[fa1]-=x;
num[fa2]++; //在集合2中加入数x
s[fa2]+=x;
a[x]=++a[0]; //原来的a[x]无效,只是做下面的数连接到根的桥梁
fa[a[x]]=fa2;
}

void init() {
memset(a,0,sizeof(a));
memset(fa,0,sizeof(fa));
memset(s,0,sizeof(s));
memset(num,0,sizeof(num));
for(int i=1; i<=n; i++) {
a[i]=fa[i]=s[i]=i;
num[i]=1;
}
a[0]=n;
}

int main() {

while(~scanf("%d%d",&n,&m)) {
init();
for(int i=1; i<=m; i++) {
int opr;
scanf("%d",&opr);
if(opr==1) {
int x,y;
scanf("%d%d",&x,&y);
Union(a[x],a[y]);
}
if(opr==2) {
int x,y;
scanf("%d%d",&x,&y);
Remove(x,y);
}
if(opr==3) {
int x;
scanf("%d",&x);
int fa=find(a[x]);
printf("%d %lld\n",num[fa],s[fa]);
}
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  uva 蓝书 并查集