LOJ2294 银河英雄传说 题解

发布时间 2024-01-06 12:50:06作者: Martian148

Question

LOJ2294 银河英雄传说

Solution

算是带权并查集一个比较典的题目了

定义 \(d[x]\) 表示战舰 \(x\)\(fa[x]\) 之间边的权值,在路径压缩把 \(x\)\(fa[x]\) 修改为根节点时,把 \(d[x]\) 更新成从 \(x\) 到树根的路径上的所有边权和

然后收到 C x y 的指令时,分别执行 getfa(x)getfa(y) 来查询和路径压缩,如果两个在一个集合中,就返回 \(|d[x]-d[y]|-1\)

考虑合并 M x y 我们把 \(x\) 的祖先 \(fx\) 作为 \(fy\) 的儿子,那么 \(fx\)\(fy\) 的距离就是 \(fy\) 这个集合的大小,所以我们需要另外开一个数组 \(siz[x]\) 记录以 \(x\) 为树根的子树的大小,并在合并时更新

Code

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
inline char read_ch(){
	char ch=getchar();
	while(ch!='M'&&ch!='C')ch=getchar();
	return ch;
}
struct dsu{
    int n;
    vector<int> fa,d,siz;
    dsu(int n){
        this->n=n;
        fa.resize(n+1);d.resize(n+1);siz.resize(n+1);
        for(int i=1;i<=n;i++) fa[i]=i,d[i]=0,siz[i]=1;
    }
    int getfa(int x){
        if(fa[x]==x) return x;
        int root=getfa(fa[x]);
        d[x]+=d[fa[x]];
        return fa[x]=root;
    }
    void merge(int x,int y){
        int fx=getfa(x),fy=getfa(y);
        if(fx==fy) return ;
        fa[fx]=fy; // 把 x 的祖先连接到 y 的祖先上面去
        d[fx]=siz[fy];
        siz[fy]+=siz[fx];
    }
    int query(int x,int y){
        int fx=getfa(x),fy=getfa(y);
        if(fx!=fy) return -1;
        return abs(d[x]-d[y])-1;
    }
};

int main(){
    int Q;Q=read();
    dsu d(30000);
    while(Q--){
        char ch=read_ch();
        int x=read(),y=read();
        if(ch=='M') d.merge(x,y);
        else printf("%d\n",d.query(x,y));
    }
    return 0;
}