• [NOI2002]银河英雄传说


    银河英雄传说

    这是一道带权并查集的题目。

    首先,按照题目要求,我们可以很容易的想到用并查集来实现。但是我们会发现,如果只用并查集记录队列的合并情况,那么就无法满足C操作,所以我们需要在维护并查集的同时,维护每个结点的信息。

    这道题目需要查询是否在一个队列中,也就是是否在一个集合中,是并查集的基本操作,写一个find函数就可以了。但是第二个要求是一个并查集中两个结点的距离,那么我们就需要一个d数组,记录结点i到fa[i]的距离,那么询问的时候就可以输出abs(d[x]-d[y])-1。

    那接下来的问题就是怎么维护这个d数组呢?首先我们可以明确的一点是需要在查询、合并并查集的时候维护,那我们想一想,并查集在合并的时候,会把一个并查集的头结点和另一个并查集的头结点合并,但是题目的要求是,把一个并查集的头部接到另一个并查集的尾部,所以我们就需要知道并查集的大小,我们用siz表示,这样的话每次合并我们就只需要做和就可以了,因为结点到新队列头结点的距离=当前节点原来到队头的距离+新并查集的大小。如果不理解的话,我们可以看一下下面的图:

    当我们要合并x和y的时候,我们会把两个并查集的头结点连接起来,所以x中子节点的d就可以表示为y的大小+它到x头结点的距离。

    下面是AC代码:

    #include<bits/stdc++.h>
    #define R register int
    #define M 500500
    using namespace std;
    int t,fa[M],d[M],siz[M];
    //d 表示飞船i到队头的距离  siz 表示飞船所在并查集的大小 
    char ch;
    inline int find(int x){
        if(fa[x]!=x){
            int k=fa[x];
            fa[x]=find(fa[x]);
            d[x]+=d[k]; //当前结点到新队列头结点的距离=当前节点原来到队头的距离+队头到新队列头的距离 
            siz[x]=siz[fa[x]]; //更新当前集合的大小 
        }
        return fa[x];
    }
    inline void add(int x,int y){
        int fx=find(x),fy=find(y);
        fa[fx]=fy;
        d[fx]=d[fy]+siz[fy]; //前结点到新队列头结点的距离=当前节点原来到队头的距离+新并查集的大小 
        siz[fy]+=siz[fx];
        siz[fx]=siz[fy];
    }
    inline int query(int x,int y){return find(x)!=find(y) ? -1 : abs(d[x]-d[y])-1;}
    int main(){
        ios::sync_with_stdio(0);
        int x,y;
        cin>>t;
        for(R i=1;i<=30000;++i) fa[i]=i,siz[i]=1;
        while(t--){
            cin>>ch>>x>>y;
            if(ch=='M') add(x,y);
            else cout<<query(x,y)<<endl;
        }
        return 0;
    }
  • 相关阅读:
    Mina、Netty、Twisted一起学(七):公布/订阅(Publish/Subscribe)
    MySQL高可用之——keepalived+互为主从
    JS之BOM
    Mac下利用(xcode)安装git
    计算矩阵边缘元素之和
    什么是猴子补丁(monkey patch)
    协议支撑
    BZOJ 3727 PA2014 Final Zadanie 树形DP
    Linux cat命令
    iOS8新特性
  • 原文地址:https://www.cnblogs.com/Glacier-elk/p/9769375.html
Copyright © 2020-2023  润新知