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\n", id[query(x, 1, n, y)]); } else { merge(root[find(x)], root[find(y)]); fa[find(x)]=find(y); } } return 0; }