数据结构训练之一

https://www.luogu.org/problem/P3224

题目特性:

动态维护,询问范围第k大

能够想到的是并查集权值线段树,但是它是动态维护啊,会加边啊,怎么办?

权值线段树合并,并查集维护,动态开点保证空间复杂度

清晰明了的代码

code:

#include <cctype>
#include <cstdio>
using namespace std;
const int maxn=1e5+5, maxseg=maxn*20;//动态开点线段树空间是NlogN 
int n, m, q, v[maxn], id[maxn], fa[maxn];
int cnt, root[maxn], lc[maxseg], rc[maxseg], seg[maxseg];
void get(int &x){
    int flag=1; char c;
    for (c=getchar(); !isdigit(c); c=getchar())
        if (c=='-') flag=-1;
    for (x=c-48; c=getchar(), isdigit(c); )
        x=(x<<3)+(x<<1)+c-48; x*=flag;
}
int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); }
void add(int &x, int l, int r, int pos){
    if (!x) x=cnt++;
    if (l==r){ ++seg[x]; return; }
    int mid=(l+r)>>1;
    if (pos>mid) add(rc[x], mid+1, r, pos);
    else add(lc[x], l, mid, pos);
    seg[x]=seg[lc[x]]+seg[rc[x]];
}
int merge(int x, int y){
    if (!x) return y; if (!y) return x;
    lc[y]=merge(lc[x], lc[y]);
    rc[y]=merge(rc[x], rc[y]);
    seg[y]=seg[lc[y]]+seg[rc[y]];
    return y;
}
int query(int now, int l, int r, int k){
    if (l==r) return l;
    int mid=(l+r)>>1;
    if (seg[lc[now]]>=k) return query(lc[now], l, mid, k);
    else return query(rc[now], mid+1, r, k-seg[lc[now]]);
}
int main(){
    get(n); get(m);
    for (int i=1; i<=n; ++i){
        get(v[i]); id[v[i]]=i;
        fa[i]=i; root[i]=cnt++;
        add(root[i], 1, n, v[i]);
    }
    int t1, t2;
    for (int i=0; i<m; ++i){
        get(t1); get(t2);
        merge(root[find(t1)], root[find(t2)]);
        fa[find(t1)]=find(t2);
    }
    get(q); char c; int x, y;
    for (int i=0; i<q; ++i){
        while (!isgraph(c=getchar()));
        get(x); get(y);
        if (c=='Q'){
            x=root[find(x)];
            if (seg[x]<y) puts("-1");
            else printf("%d
", id[query(x, 1, n, y)]);
        } else {
            merge(root[find(x)], root[find(y)]);
            fa[find(x)]=find(y);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wzxbeliever/p/11645847.html