NOI2002 银河英雄传说

传送门

这道题很明显是一道并查集 + 维护并查集中元素到根结点之间距离的题。

具体的维护方法也很简单。fa数组表示每个点的父亲,dis数组表示当前节点到父亲的距离,sum数组表示当前节点所在集合中元素个数。预处理的时候把每个点的dis设为0,sum设为1.

之后对于每次战舰合并操作,将其中一个战舰所在集合合并至另一个战舰所在集合,被合并的集合的dis要加上另一个的sum,另一个集合的sum加上被合并集合的sum,并把被合并的集合的sum清零。询问操作就是先求出两艘战舰之间dis之差的绝对值,之后再-1.

然后对于找父亲的操作,我们比普通的并查集多了一个维护距离,只要先更新自己的父亲的dis,之后对于自己,则有dis[x] += dis[fa[x]]即可。

这个还是很好理解的……上代码。(好像这题用我的快读有bug……?没办法用了cin)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
const int M = 30005;
typedef long long ll;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
    }
    return ans * op;
}

char c;
int t,fa[M+5],dis[M+5],num[M+5],a,b;

int getfa(int x)
{
    if(fa[x] == x) return fa[x];
    int fx = getfa(fa[x]);
    dis[x] += dis[fa[x]];
    return fa[x] = fx;
}

int main()
{
//    freopen("a.in","r",stdin);
    rep(i,1,M) fa[i] = i,num[i] = 1;
    cin >> t;
    while(t--)
    {
        cin >> c >> a >> b;
        int r1 = getfa(a),r2 = getfa(b);
        if(c == 'M')
        {
            dis[r1] += num[r2],fa[r1] = r2;
            num[r2] += num[r1],num[r1] = 0;
        }
        if(c == 'C')
        {
            if(r1 != r2) printf("-1\n");
            else printf("%d\n",abs(dis[a] - dis[b]) - 1);
        }
    }
    return 0;
}

 

posted @ 2018-08-30 22:32  CaptainLi  阅读(176)  评论(0编辑  收藏  举报