Luogu P1196 [NOI2002]银河英雄传说

  一年没写博客了(滑稽)。

  这道题很玄学,导致自己都有一个坑人的问题求解。如果有大佬有能力求帮助:https://www.luogu.org/discuss/show?postid=30231

  再来讲一下我对这道题的理解吧。

  首先,对于判断两个数是否在同一列战舰中,我们只需要朴素的并查集就可以。

  但这道题还要求我们去搞一个两船之间的距离(这就很难办了),所以只能使用带权并查集。

  我们开一个数组front[],来表示第i号战舰到i所在的战舰的队头的距离。这样如果两个战舰在同一列中,他们的距离就是abs(front[i]-front[j])-1(因为这两辆都不包括,因此-1)。

  这样是很方便,但当我们合并时,就会发生front[]值失去对应性。所以我们要不断地更新front[]。

  再开一个数组len[]表示以i为队头的队列的长度,这样在把i接到j后面时,只需要让front[fi]+=len[fj],并把len[fj]清零即可。

  最后对于front[]的更新,只需要在getfather时回溯完成即可。

  然后你就学会了如何打带权并查集(欧耶)!

  CODE

#include<cstdio>
using namespace std;
const int N=30005;
int father[N],front[N],len[N],i,t,x,y;
inline void read(int &x)
{
    x=0; char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
inline void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    if (x/10) write(x/10);
    putchar(x%10+'0');
}
inline int getfather(int k) 
{
    if (father[k]==k) return k;
    int fa=getfather(father[k]);
    front[k]+=front[father[k]];
    return father[k]=fa;
}
inline int abs(int a) { return a<0?-a:a; }
int main()
{
    //freopen("testdata.in","r",stdin); freopen("my_out.out","w",stdout);
    for (i=1;i<N;++i)
    father[i]=i,len[i]=1;
    read(t);
    while (t--)
    {
        char ch=getchar();
        while (ch!='M'&&ch!='C') ch=getchar();
        read(x); read(y);
        int fx=getfather(x),fy=getfather(y);
        if (ch=='M') 
        {
            front[fx]+=len[fy];
            father[fx]=fy;
            len[fy]+=len[fx];
            len[fx]=0;
        } else write(fx==fy?abs(front[x]-front[y])-1:-1),putchar('
');
    }
    return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/8178505.html