带权并查集浅谈

笔者最近刷到了一道带权并查集的题目,当做入门博客写一篇学习笔记吧。

题目链接

题目中要求求距离+换爹,很显然,暴力的dfs在20000的数据下绝对会被卡的。

自然想到,什么数据结构可以快速维护父子关系?

显然有并查集。

那么,我们要做的就是修改并查集,使它能够维护距离了。

那么,我们在每一次更新父亲(路径压缩)的时候更新距离。

先来看最基本的find函数:

inline int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}

我们要在这时候维护距离dis的改变。

于是,我们先记录下路径压缩前的父亲。

然后压缩。

返回的时候,当前的结点要累加之前的距离即可。

代码:

inline int find(int x){
    if(x==f[x])return x;
    int t=f[x];//记录之前父亲 
    f[x]=find(f[x]);//并查集find 
    dis[x]+=dis[t];//累加距离 
    return f[x];
}

对于多组训我,切记清空数组。并查集的初始化不要忘记。

将dis数组初始化为0,f数组照常赋值成自己。

对于第一个操作,直接认爹,然后更新距离。

所以,对于第二个E操作,在询问的时候要find一下,以便于更新之前没有更新的距离。之后直接输出即可。

注意,dis[x]维护的是x到根的距离。

并查集是树形结构。

总代码:

#include<cstdio>//加权并查集 
#include<iostream>
#include<cstring>
using namespace std;
int dis[50000],f[50000];
int n,x,y,t;
char s[50];
/*st
struct node{
    int next,to;
}; 
head[5000];
inline void add(int x,int y){
    e[++tot].to=y;
    e[tot].next=head[x];
    head[x]=tot;
}*/
inline int abs(int x){return x<0?-x:x;}
inline void read(int &x){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')w=-1;
        ch=getchar();
    }while(ch>='0'&&ch<='9'){
        s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    }x=s*w;
}
inline int find(int x){
    if(x==f[x])return x;
    int t=f[x];//记录之前父亲 
    f[x]=find(f[x]);//并查集find 
    dis[x]+=dis[t];//累加距离 
    return f[x];
}
inline void init(){
    memset(dis,0,sizeof(dis));
    for(int i=1;i<=n;++i)f[i]=i;
}
int main(){
    read(t);
    while(t--){
        read(n);
        init();
        while(1){
            cin>>s;
            if(s[0]=='O')break;
            if(s[0]=='I'){
                read(x);
                read(y);
                f[x]=y;
                dis[x]+=abs(x-y)%1000;
            }else{
                read(x);
                find(x);
                printf("%d\n",dis[x]);
            }
        }
    }
    return 0;
}

留一道例题,笔者之后补题解。

题目链接(种类并查集)

题解

例题2

posted @ 2019-07-09 16:47  Refined_heart  阅读(807)  评论(0编辑  收藏  举报