bzoj2733 [HNOI2012]永无乡

bzoj2733
对于操作B 就用并查集看是否已经联通,若未联通就把两棵splay合并..
两棵splay的合并,启发式合并?(其实就是暴力合并【捂脸】)
把个数少的往个数大的里面一个个插入…..

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 110000
int f[N],a[N],b[N],n,m,cnt=0,fa[N],size[N],ch[N][2],root[N],q,data[N];
int find(int x){
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
void update(int p){size[p]=size[ch[p][0]]+size[ch[p][1]]+1;}
void rotate(int &x){
    int y=fa[x],z=fa[y],t=ch[y][0]==x;
    if(z) ch[z][ch[z][1]==y]=x;
    fa[y]=x;fa[x]=z;fa[ch[x][t]]=y;
    ch[y][t^1]=ch[x][t];ch[x][t]=y;
    update(y);update(x);
}
void splay(int x){
    while(fa[x]){
        int y=fa[x];
        if(!fa[y]){rotate(x);return;}
        if(ch[y][0]==x^ch[fa[y]][0]==y) rotate(x);
        else rotate(y);rotate(x);
    }
}
void insert(int &p,int x,int ff){
    if(!p){
        p=x;fa[p]=ff;size[p]=1;ch[p][0]=ch[p][1]=0;splay(p);
        return ;
    }if(a[x]<a[p]) insert(ch[p][0],x,p);
    else if(a[x]>a[p]) insert(ch[p][1],x,p);
}
void merge(int xx,int yy){
    int x=find(xx),y=find(yy);
    if(x==y) return;
    if(size[root[x]]<size[root[y]]) swap(x,y);//y往x中插 
    int op=0,cl=1;data[1]=root[y];
    while(op<cl){
        int p=data[++op];
        if(ch[p][0]) data[++cl]=ch[p][0];
        if(ch[p][1]) data[++cl]=ch[p][1];
        insert(root[x],p,0);root[x]=p;
    }f[y]=x;
}
int query(int p,int x){
    if(x>size[p]) return -1;
    if(size[ch[p][0]]+1==x) return p;
    if(x<=size[ch[p][0]]) return query(ch[p][0],x);
    return query(ch[p][1],x-size[ch[p][0]]-1);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        f[i]=i;root[i]=i;
        size[i]=1;fa[i]=ch[i][0]=ch[i][1]=0;
    }
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        merge(x,y);
    }scanf("%d",&q);
    for(int i=1;i<=q;i++){
        char ch[5];int x,y;scanf("%s%d%d",ch,&x,&y);
        if(ch[0]=='B') merge(x,y);
        else printf("%d\n",query(root[find(x)],y));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值