【Acwing并查集】238. 银河英雄传说

238. 银河英雄传说 - AcWing题库

题意:

【Acwing并查集】238. 银河英雄传说_第1张图片

思路:

并查集维护两个信息:每个连通块的size和每个结点之间的距离

对于连通块的size,只需要在合并的时候维护一下就好了

对于每个结点之间的距离,我们考虑类似于树上差分的思想,先处理每个结点离根节点的距离,再差分减一下就好了

那么问题就转化成维护每个结点离根节点的距离

由于维护的信息的主体是结点,那么就不能只在合并的时候维护了,在find的时候也需要维护

我们将连通块v接到连通块u下面时,更新连通块v的每个结点离根节点的距离,因为根节点换了,同时也要更新连通块的size

更新连通块v每个结点的信息是通过find回溯的时候更新的,我们给u的原先的根节点+=size(u)就行,这样在之后如果访问连通块v的某个结点时,find在回溯时会把这个结点和连通块v原先根节点之间的所有结点都更新,同时也将这些结点路径压缩,这有点像线段树懒标记的感觉

路径压缩后,虽然原先的链式结构被破坏,但是距离信息保留在d数组中,因此查询的时候直接查询d[x]就好了,d数组相当于是前缀和数组

如果之后又要合并已经被路径压缩的结点,也是同样的道理,先更新连通块根节点(相当于打个标记),然后在查询的时候回溯下传求前缀和就好了

Code:

#include 
using namespace std;
const int mxn=3e4+10;
string op;
int n,x,y;
int f[mxn],sz[mxn],d[mxn];
int find(int x){
    if(f[x]!=x){
        int rt=find(f[x]);
        d[x]+=d[f[x]];
        f[x]=rt;
    }
    return f[x];
}
void join(int u,int v){
    int f1=find(u),f2=find(v);
    if(f1!=f2){
        f[f1]=f2;
        d[f1]=sz[f2];
        sz[f2]+=sz[f1];
    }
}
int main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin>>n;
    for(int i=1;i>op>>x>>y;
        if(op=="M"){
            join(x,y);
        }else{
            int a=find(x),b=find(y);
            if(a!=b) cout<<-1<<'\n';
            else cout<

总结:

当要我们维护联通块信息、涉及到分类、判环、维护传递性关系时都可以用并查集

在写并查集之前先考虑好并查集维护的对象

并查集维护两种信息:连通块信息和结点信息

维护连通块信息时,只需在合并的时候维护就好了,维护完连通块之后可以考虑缩点

维护结点信息时,要在find和merge时一起维护,在merge的时候给根节点打标记,在find的时候回溯下传标记

你可能感兴趣的:(并查集,算法,c++,数据结构)