Almost Union-Find(带权并查集)

Almost Union-Find(带权并查集)_第1张图片

题意:有n个数,刚开始每个数都是一个单独的集合里,给定下列三种操作

1 p q :将p 和 q 合并在一个集合里

2 p q :将p 放在 q所在的集合里

3 p :  输出p 所在的集合的元素个数和元素的和。

思路:对于1操作  如果p q 祖先不同,那么merge(p,q)即可;对于3 操作,只需找到p的祖先,然后输出这个集合的元素个数和元素的和,对于 2 操作,我们不能直接合并 p q ,因为这样 p的子孙们也归到的q中 这是不符合题意的,在这里,我们 需要新开一个结点,让它单独成为一个集合,并且让 p 指向这个结点,这样 我们操作的时候 是对 这个结点进行操作,而不改变 原先p 的位置,我们定义一个 数组idx【i】 它表示 i指向的结点。这样的话 将idx【i】与 q合并, 同时 在p的集合里删去p这个点即可。详情见代码:

#include
#include
#include
#define inf 0x3f3f3f3f
#define LL long long
#include
using namespace std;
int f[100010],idx[100010],num[100010],sum[300010];
int n,m;
void init()
{
    for(int i=1; i<=100010; i++)
    {
        f[i]=i;
        idx[i]=i;
        num[i]=1;
        sum[i]=i;
    }
    return ;
}
int getf(int v)
{
    if(f[v]==v)
        return v;
    int s=getf(f[v]);
    sum[v]=sum[v]+sum[f[v]];
    num[v]=num[v]+num[f[v]];
    return f[v]=s;
}
void merge(int u,int v)
{
    int t1=getf(idx[u]);
    int t2=getf(idx[v]);
    if(t1!=t2)
    {
        f[t2]=t1;
        sum[t1]=sum[t1]+sum[t2];
        num[t1]=num[t1]+num[t2];
    }
    return ;
}
void build(int p)
{
    int k=++n;
    idx[p]=k;
    f[k]=k;
    sum[k]=p;
    num[k]=1;
}
int main()
{
    int x,p,q;
    while(~scanf("%d %d",&n,&m))
    {
        init();
        for(int i=0; i

 

你可能感兴趣的:(Almost Union-Find(带权并查集))