https://www.luogu.org/problem/P3224
题目特性:
动态维护,询问范围第k大
能够想到的是并查集,权值线段树,但是它是动态维护啊,会加边啊,怎么办?
权值线段树合并,并查集维护,动态开点保证空间复杂度;
清晰明了的代码
code:
#include
#include
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