[BZOJ][平衡树+启发式合并][替罪羊树]2733: [HNOI2012]永无乡

时间:2021-12-06 20:42:51

2733: [HNOI2012]永无乡
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 4444 Solved: 2378
[Submit][Status][Discuss]
Description
永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。
Input
输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000
对于 100%的数据 n≤100000,m≤n,q≤300000
Output
对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表 示所询问岛屿的编号。如果该岛屿不存在,则输出-1。
Sample Input
5 1
4 3 2 5 1
1 2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3
Sample Output
-1
2
5
1
2
HINT
Source

看看数据大小估一下复杂度2个log。然后就想到了平衡树+启发式合并,具体实现的话我用了替罪羊树。
为什么要用替罪羊树呢?因为可以进第一页啊

代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
const double alpha=0.75;
inline char tc(void){
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline int read(void){
    int a=0;char c;
    while((c=tc())<'0'||c>'9');
    while(c>='0'&&c<='9')a=a*10+c-'0',c=tc();
    return a;
}
inline char gc(void){
    char c;
    while((c=tc())=='\n'||c=='\r'||c==' ');
    return c;
}
int o,n,m,q,f[100001],v[100001],sum[100001],rt[100001],size[100001],ch[100001][2],que[100001],tot,*sgt;
int queo[100001],cnt;
int gf(int x){
    return x==f[x]?x:f[x]=gf(f[x]);
}
inline void update(int x){
    size[x]=1+size[ch[x][0]]+size[ch[x][1]];
}
inline bool isbad(int x){
    if(size[x]*alpha+5<max(size[ch[x][0]],size[ch[x][1]]))return 1;
    return 0;
}
void dfs(int x){
    if(ch[x][0])dfs(ch[x][0]);
    que[++tot]=x;
    if(ch[x][1])dfs(ch[x][1]);  
}
int build(int l,int r){
    if(l>r)return 0;
    int mid=l+r>>1,x=que[mid];
    size[x]=1,
    ch[x][0]=build(l,mid-1),
    ch[x][1]=build(mid+1,r);
    update(x);
    return x;
}
inline void rebuild(int *x){
    tot=0,dfs(*x),*x=build(1,tot);
    return ;
}
void insert(int &x,int a){
    int w=x;
    if(x==0){
        x=a,ch[a][0]=ch[a][1]=0;
        update(a);
        return ;
    }
    if(v[a]<=v[x])insert(ch[x][0],a);
    else insert(ch[x][1],a);
    update(x);
    if(isbad(x))sgt=&x;
    return ;
}
void ins(int &x,int a){
    sgt=NULL;
    insert(x,a);
    if(sgt)rebuild(sgt);
}
void dfs1(int x){
    if(ch[x][0])dfs(ch[x][0]);
    queo[++cnt]=x;
    if(ch[x][1])dfs(ch[x][1]);
}
void merge(int a,int b){
    a=gf(a),b=gf(b);
    if(a==b)return ;
    if(sum[a]>sum[b])swap(a,b);
    cnt=0,dfs1(rt[a]);
    for(int i=1;i<=cnt;++i)ins(rt[b],queo[i]);
    rt[a]=0,f[a]=b,sum[b]+=sum[a];
}
int find(int x,int k,int sum){
    if(size[x]<k)return -1;
    if(k<=size[ch[x][0]])return find(ch[x][0],k,sum+1);
    else if(k==size[ch[x][0]]+1)return x; 
    else return find(ch[x][1],k-size[ch[x][0]]-1,sum+1);
}
int main(void){
    register int i,x,y;
    n=read(),m=read();
    for(i=1;i<=n;++i)v[i]=read(),f[i]=i,sum[i]=1,rt[i]=i,size[i]=1;
    for(i=1;i<=m;++i)x=read(),y=read(),merge(x,y);
    q=read();
    while(q--)
        if(gc()=='B')x=read(),y=read(),merge(x,y);
        else x=read(),y=read(),printf("%d\n",find(rt[gf(x)],y,0));
    return 0;
}