带权并查集 NOI 2002 银河英雄传说

版权声明:未经本蒟蒻同意,请勿转载本蒻博客 https://blog.csdn.net/wddwjlss/article/details/82289792

题意:有 n 艘战舰,一开始排成一行,第 i 号战舰在第 i 列,两种操作:
1. M i j 表示将第 i 号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所在的战舰队列的尾部。
2. C i j 表示询问第 i 号战舰与第 j 号战舰当前是否在同一列中,如果在同一列中,那么它们之间有多少战舰。

这题一看就是并查集,但怎么维护两艘战舰之间有多少战舰呢?这里就要使用带权并查集了。 v a l [ i ] 表示飞船 i 到其所在队列队头的距离(类似一个前缀和的东西), n u m [ i ] 表示以 i 为队头的队列的飞船数量。具体说明看代码

#include<bits/stdc++.h>
using namespace std;
int val[1001000],f[1001000],num[1001000];
int find(int x)
{
    if(x==f[x])
        return x;
    if(x!=f[x])
    {
        int g=f[x];
        f[x]=find(f[x]);
        val[x]+=val[g];//更新当前节点到根的距离
        return f[x];
    }
}
int merge(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
    {
        f[fx]=fy;
        val[fx]=val[fy]+num[fy];//更新合并之后fx到队头的距离
        num[fy]+=num[fx];//更新以fy为队头的队列的飞船数量
        num[fx]=0;//以fx为队头的队列数量清零
    }
}
char s[5];
int main()
{
    int t;
    cin>>t;
    for(int i=1;i<=35000;i++) 
    {
        f[i]=i;
        num[i]=1;
    }
    while(t--)
    {
        scanf("%s",s+1);
        if(s[1]=='M')
        {
            int a,b;
            scanf("%d%d",&a,&b);
            merge(a,b);
        }
        if(s[1]=='C')
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(find(a)!=find(b))
                cout<<"-1"<<endl;
            else
                cout<<abs(val[a]-val[b])-1<<endl;       
        }
    }
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/wddwjlss/article/details/82289792