永无乡[HNOI2012]

题目描述

永无乡包含 n 座岛,编号从 1 到 n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,

通过桥可以从一个岛到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以 到达岛 b ,则称岛 a 和岛 b 是连通的。

现在有两种操作:

B x y 表示在岛 x 与岛 y 之间修建一座新桥。

Q x k 表示询问当前与岛 x 连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪座,请你输出那个岛的编号。

输入格式

第一行是用空格隔开的两个正整数 n 和 m ,分别表示岛的个数以及一开始存在的桥数。

接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi ,

表示一开始就存在一座连接岛 ai 和岛 bi 的桥。

后面剩下的部分描述操作,该部分的第一行是一个正整数 q ,表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的 格式如上所述,

以大写字母 Q 或 B开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。

输出格式

对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出 -1 。

 

 

平衡树启发式合并

最重要的是这个东西

rt[i]表示以i为根的集合的平衡树的根

可以预估,每个点最多会合并log N次

不过可以回收节点,减免空间

inline void dfs(int u,int y)
{
    if(ls)dfs(ls,y);
    if(rs)dfs(rs,y);
    insert(val[u],y,u);
}

inline void merge(int x,int y)
{
    if(size[rt[x]]>size[rt[y]])x^=y^=x^=y;
//将小的合并到大的 f[x]
=y;
dfs(rt[x],y); }

 

 

并查集维护连通性

完整代码

#include<bits/stdc++.h>
#define re return
#define ll long long
#define ls ch[u][0]
#define rs ch[u][1]
#define R register 
#define inc(i,l,r) for(int i=l;i<=r;++i)
const int maxn=100005,inf=2147483647;
using namespace std;
template<typename T>inline void rd(T&x)
{
    char c;bool f=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
    x=c^48;
    while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
    if(f)x=-x;
}

int n,m,q,tot;
int ch[maxn][2],val[maxn],size[maxn],fa[maxn],rt[maxn];
int f[maxn],import[maxn];

inline void pushup(int u){size[u]=size[ls]+size[rs]+1;}
inline bool chk(int x){re ch[fa[x]][1]==x;}

inline void rotate(int x)
{
    int y=fa[x],z=fa[y],f=chk(x),w=ch[x][f^1];
    ch[z][chk(y)]=x; fa[x]=z;
    ch[y][f]=w;fa[w]=y;
    ch[x][f^1]=y; fa[y]=x;
    pushup(y),pushup(x);
}

inline void splay(int x,int goal=0)
{
    
    while(fa[x]!=goal)
    {
        int y=fa[x],z=fa[y];
        if(z!=goal)
        chk(y)==chk(x)?rotate(y):rotate(x);
        rotate(x);
    }
}

inline void insert(int x,int goal,int ytot=0)
{
    int u=rt[goal],p=0;
    while(u)p=u,u=ch[u][x>val[u]];
    
    if(!ytot)u=++tot;
    else u=ytot;
    if(p)ch[p][x>val[p]]=u;
    size[u]=1;
    val[u]=x;
    fa[u]=p;
    ls=rs=0;
    splay(u);
    rt[goal]=u;
}

inline int kth(int k,int goal)
{
    if(k>size[rt[goal]])re -1;
    int u=rt[goal];
    while(2333)
    {
        if(size[ls]+1<k)k=k-size[ls]-1,u=rs;
        else if(size[ls]<k)re u;
        else u=ls;
    }
}

inline void dfs(int u,int y)
{
    if(ls)dfs(ls,y);
    if(rs)dfs(rs,y);
    insert(val[u],y,u);
}

inline void merge(int x,int y)
{
    if(size[rt[x]]>size[rt[y]])x^=y^=x^=y;
    f[x]=y;
    dfs(rt[x],y);
}

inline int find(int x)
{
    re x==f[x]?x:f[x]=find(f[x]);
}
int main()
{
//    freopen("in.txt","r",stdin);
    int x,y;
    rd(n),rd(m);
    inc(i,1,n)
    {
        rd(import[i]);    
        f[i]=i; 
    }
    inc(i,1,m)
    {
        rd(x),rd(y);
        int f1=find(x),f2=find(y);
        if(f1==f2)continue;
        else f[f1]=f2;
    }
    
    inc(i,1,n)
        insert(import[i],find(i));
    
    char opt[2];
    rd(q);
    while(q--)
    {
        scanf("%s",opt);
        rd(x),rd(y);
        if(opt[0]=='B')
        {
            int f1=find(x),f2=find(y);
            if(f1!=f2)merge(f1,f2);
        }
        else 
        {
            int f1=find(x);
            f1=kth(y,f1);

            if(f1==-1)printf("-1\n");
            else 
            printf("%d\n",f1);
        }
    }
    re 0;
} 

 

posted @ 2019-08-12 19:04  凉如水  阅读(144)  评论(0)    收藏  举报