BZOJ 2733 [HNOI2012]永无乡 (权值线段树启发式合并+并查集)

时间:2021-09-15 09:45:19

题意:

n<=1e5的图里,在线连边、查询某连通块第k大

思路:

练习线段树合并的好题,因为依然记得上一次启发式合并trie的时候内存爆炸的恐怖,所以这次还是用了动态开点、回收

听说启发式合并splay更快QAQ,学会了试试

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<deque>
#include<set>
#include<vector>
#include<map>
#include<functional>
     
#define fst first
#define sc second
#define pb push_back
#define mp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lc root<<1
#define rc root<<1|1
#define lowbit(x) ((x)&(-x))
 
using namespace std;
 
typedef double db;
typedef long double ldb;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PI;
typedef pair<ll,ll> PLL;
 
const db eps = 1e-6;
const int mod = 1e9+7;
const int maxn = 1e5+100;
const int maxm = 6e6+100;
const int inf = 0x3f3f3f3f;
const db pi = acos(-1.0);

int n,m;
int q;
int ls[maxn*66],rs[maxn*66],dat[maxn*66];
int root[maxn];
int a[maxn],id[maxn];

int f[maxn];
int find(int x){
    return f[x]==x?x:f[x]=find(f[x]);
}

int tot;
queue<int>pool;
int New(){
    if(!pool.empty()){
        int x = pool.front();
        pool.pop();
        return x;
    }
    ++tot;
    return tot;
}
void del(int x){
    if(!x)return;
    pool.push(x);
    return;
}
int build(int l, int r, int x){
    int mid = (l+r)>>1;
    int p = New();
    dat[p]=1;
    if(l==r)return p;
    if(x<=mid)ls[p]=build(l,mid,x);
    else rs[p]=build(mid+1,r,x);
    return p;
}
int merge(int p, int q){// leave p
    if(!p)return q;
    if(!q)return p;
    ls[p]=merge(ls[p],ls[q]);
    rs[p]=merge(rs[p],rs[q]);
    dat[p]+=dat[q];
    ls[q]=rs[q]=dat[q]=0;
    del(q);
    return p;
}
int query(int x, int l, int r, int k){
    int mid = (l+r)>>1;
    if(l==r)return l;
    if(dat[ls[x]]>=k){
        return query(ls[x],l,mid,k);
    }
    else{
        return query(rs[x],mid+1,r,k-dat[ls[x]]);
    }
}
int main() {
    scanf("%d %d" ,&n, &m);
    for(int i = 1; i <= n; i++){
        f[i]=i;
        scanf("%d", &a[i]);
        id[a[i]]=i;
        root[i]=build(1,n,a[i]);
    }
    /*for(int i = 1; i <= n; i++){
        printf("-- %d root::%d\n",i,root[i]);
    }
    for(int i = 1; i <= tot; i++){
        //printf("%d ==== %d %d %d\n",i,ls[i],rs[i],dat[i]);
    }*/
    for(int i = 1; i <= m; i++){
        int x,y;
        scanf("%d %d" ,&x, &y);
        int t1 = find(x);
        int t2 = find(y);
        if(t1!=t2){
            root[t1]=merge(root[t1],root[t2]);
            f[t2]=t1;
        }
    }
    scanf("%d", &q);
    while(q--){
        char op[5];
        int x,y;
        scanf("%s %d %d", op,&x,&y);
        if(op[0]=='Q'){
            int t = find(x);
            if(dat[root[t]]<y){printf("-1\n");continue;}
            else printf("%d\n",id[query(root[t],1,n,y)]);
        }
        else{
            int t1 = find(x);
            int t2 = find(y);
            if(t1!=t2){
                root[t1]=merge(root[t1],root[t2]);
                f[t2]=t1;
            }
        }
    }
    return 0;
}
/*
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
 */