[HNOI2012]永无乡

永无乡包含 n n 座岛,编号从 1 n n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 1 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 a a 出发经过若干座(含 0 座)桥可以 到达岛 b b ,则称岛 a 和岛 b b 是连通的。

现在有两种操作:

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

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

查询第 k 大直接平衡树。连接就是合并两个平衡树。
并查集维护一下在不在一棵树中,注意并查集的按秩合并 和 treap t r e a p 的启发式合并要同步,然后更改根那里记得写&.

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e5+5;

typedef pair<int,int>par;
#define mp make_pair

inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x;
}

int ma[MAXN];
int fa[MAXN],rk[MAXN];

inline int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}

inline void merge_set(int x,int y){
    if((x=find(x))==(y=find(y)))return;
    if(rk[x]>rk[y])fa[y]=x,rk[x]+=rk[y];
    else {
        fa[x]=y;
        rk[y]+=rk[x];
    }
}

inline void makeset(int n){
    for(int i=0;i<=n;i++)fa[i]=i,rk[i]=1;
}

struct treap{
    int val[MAXN],prio[MAXN],lson[MAXN],rson[MAXN],size[MAXN],rt[MAXN],cnt;
    inline void pushup(int o){
        size[o]=size[lson[o]]+size[rson[o]]+1;
    } 
    inline par split(int p,int x){
        if(!x)return mp(0,p);
        int l=lson[p],r=rson[p];
        if(x<=size[l]){
            par tem=split(l,x);
            lson[p]=tem.second;pushup(p);return mp(tem.first,p);
        }
        par tem=split(r,x-size[l]-1);
        rson[p]=tem.first;pushup(p);return mp(p,tem.second);
    }
    inline int merge(int x,int y){
        if(!x){pushup(y);return y;}
        if(!y){pushup(x);return x;}
        if(prio[x]<prio[y]){
            rson[x]=merge(rson[x],y);pushup(x);return x;
        }
        lson[y]=merge(x,lson[y]);pushup(y);return y;
    }
    inline int newnode(int x){
        ++cnt;
        prio[cnt]=rand();val[cnt]=x;lson[cnt]=rson[cnt]=0;
        size[cnt]=1;
        return cnt;
    }
    int findsz(int Rt,int x){
        int p=Rt,ans=0;
        while(p){
            if(val[p]<x)ans+=size[lson[p]]+1,p=rson[p];
            else p=lson[p];
        }
        return ans;
    }
    void modify(int &a,int &b){//把a合并到b上 
        del(a,b);
        a=b;
    }
    void del(int p,int &Rt){//将a上的所有节点pi删除插入到Rt上 
        if(lson[p])del(lson[p],Rt);
        if(rson[p])del(rson[p],Rt);
        lson[p]=rson[p]=0;size[p]=1;
        ins(Rt,p);
    }
    void ins(int &Rt,int p){//修改rt 
        int sz=findsz(Rt,val[p]);
        par t1=split(Rt,sz);
        Rt=merge(merge(t1.first,p),t1.second);
    }
    void debug(int rt){
        if(lson[rt])debug(lson[rt]);
        printf("%d ",val[rt]);
        if(rson[rt])debug(rson[rt]);
    }
}T;

int n,m;

void Merge(int x,int y){
    if(T.size[T.rt[fa[x]]]<=T.size[T.rt[fa[y]]])T.modify(T.rt[fa[x]],T.rt[fa[y]]);
    else T.modify(T.rt[fa[y]],T.rt[fa[x]]);
    merge_set(x,y);
}

void query(int x,int k){
    //T.debug(T.rt[fa[x]]);puts("sm");
    if(T.size[T.rt[fa[x]]]<k){
        puts("-1");return;
    }
    par t1=T.split(T.rt[fa[x]],k-1);
    par t2=T.split(t1.second,1);
    printf("%d\n",ma[T.val[t2.first]]);
    T.rt[fa[x]]=T.merge(t1.first,T.merge(t2.first,t2.second));
}

int main(){
    srand(19260817);
    n=read(),m=read(); 
    makeset(n);
    for(int i=1;i<=n;i++){
        int tmp;
        tmp=read();ma[tmp]=i;
        T.rt[i]=T.newnode(tmp);
    }
    for(int i=1;i<=m;i++){
        int x,y;
        x=read();y=read();
        if(fa[x]!=fa[y]){
            Merge(x,y);/*   puts("gg");
            T.debug(T.rt[fa[y]]);*/
        }
    }
    int q,x,y;
    char opt[2];
    q=read();
    for(int i=1;i<=q;i++){
        scanf("%s",opt);
        x=read();y=read();
        if(opt[0]=='Q'){
            query(x,y);
        }
        else {
            if(fa[x]!=fa[y]){
                Merge(x,y);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值