NOI2002 银河英雄传说(luogu p1196)

原题链接:https://www.luogu.org/problem/show?pid=1196

luogu 400AC纪念题

 

这道题也算是对于并查集的巧妙运用了。

首先解释一下数组的含义,f[i]表示i号战舰现在正在f[i]列中,num[i]表示目前第i列上有num[i]艘战舰,front[i]表示i号战舰是当前列的第front[i]艘战舰,

因为特殊的要求,所以在路径压缩的时候就要注意,不能只简单地寻找祖先,而要在寻找祖先的同时将自己所在列的所有战舰都合并到祖先的列,也就是将祖先的front值加到当前点的front值上。

在合并的指令时,因为当前战舰不一定是最后一艘,仅凭一个front已经不够用了,因此num派上了用场。

假设fx,fy分别表示x,y的祖先,将x所在列合并到y所在列的时候,就可以直接将num[fy]加到front[fx]上,同时更新num[fy],然后fx位置已经没有战舰了,于是可以清空为0。

每次询问的时候,首先判断x,y是否在同一列中,这个用并查集就能很快完成,在同一列的话,由于front就代表了他们的位置,所以abs(front[x]-front[y])-1。

 

#include<cstdio>
int f[50005],front[50005],num[50005];
char s[5];
int abs(int x)
{
    return x<0 ? -x : x;
}
int find(int x)
{
    if(x==f[x]) return x;
    int fn=find(f[x]);
    front[x]+=front[f[x]];
    return f[x]=fn;
}
int main()
{
    int t,x,y;
    scanf("%d",&t);
    for(int i=0;i<=50000;i++)
    {
        f[i]=i;
        num[i]=1;
    }
    while(t--)
    {
        scanf("%s %d %d",s,&x,&y);
        int fx=find(x),fy=find(y);
        if(s[0]=='M')
        {
            front[fx]+=num[fy];
            f[fx]=fy;
            num[fy]+=num[fx];
            num[fx]=0;
        }
        else
        {
            if(fx!=fy) printf("-1\n");
            else printf("%d\n",abs(front[x]-front[y])-1);
        }
    }
    return 0;
}

 

posted @ 2017-09-25 19:20  Excim  阅读(139)  评论(0编辑  收藏  举报