笔者最近刷到了一道带权并查集的题目,当做入门博客写一篇学习笔记吧。
题目中要求求距离+换爹,很显然,暴力的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 ",dis[x]); } } } return 0; }
留一道例题,笔者之后补题解。