题目描述
有一张n个点的无向图,每一个点有一个
[
1
,
n
]
[1,n]
[1,n]的权值,且互不相等。
支持加入一条边,查询与某个点联通的所有点中权值第
k
k
k小的点的编号。
思路
数据结构,思想
- 线段树合并
关于线段树合并
- 在动态开点线段树上进行
- 能进行的前提,要么是在所有的线段树中,总的有意义值的数量不多(比如一共就
n
n
n个,散落在
m
m
m个线段树里),要么是一些其他特殊性质使得它可以进行。
这个算法非常考验我们的分析能力。 - 很显然,如果我们要合并两棵线段树(假设当前两个点为 n 1 n_1 n1和 n 2 n_2 n2,我们要把 n 2 n_2 n2并到 n 1 n_1 n1上面),那么若某一个孩子只有 n 1 n_1 n1或者 n 2 n_2 n2有,那我们大可以直接把 n 1 n_1 n1的那一个儿子连上去,否则就继续递归下去。
- 可以很容易证明,这一通操作的复杂度是两颗线段树都开了的节点的数量。这是绝对不超过节点个数小的那一个线段树的节点个数的。自然而然地,这可以按秩合并。
- 复杂度粗略一算是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的,但大部分情况下分析一波,会觉得这是 O ( n l o g n ) O(nlogn) O(nlogn)的。
关于这道题
- 直接线段树合并即可
- 我为了方便,还写了一个并查集来维护联通块大小即代表节点(告诉我们线段树根节点编号)
代码
#include<bits/stdc++.h>
#define MAXN 100000
using namespace std;
template<typename T>void Read(T &cn)
{
char c;int sig = 1;
while(!isdigit(c = getchar()))if(c == '-')sig = -1; cn = c-48;
while(isdigit(c = getchar()))cn = cn*10+c-48; cn*=sig;
}
template<typename T>void Write(T cn)
{
if(cn < 0) {putchar('-'); cn = 0-cn; }
int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
while(cn)wei++,cm = cm*10+cn%10,cn/=10;
while(wei--)putchar(cm%10+48),cm/=10;
putchar(cx+48);
}
struct qwe{
struct node{
int ls,rs;
int ge;
void qing() {ls = rs = ge = 0; }
};
node t[MAXN*40+1];
int tlen;
int ro[MAXN+1];
void build(int cn)
{
tlen = cn;
for(int i = 1;i<=cn;i++)t[ro[i] = i].qing();
}
void jia(int &cn,int cm,int cx,int l,int r)
{
if(!cn)t[cn = ++tlen].qing();
if(l == r){
t[cn].ge = 1;
t[cn].ls = cx;
return;
}
int zh = (l+r)>>1;
if(cm <= zh)jia(t[cn].ls,cm,cx,l,zh); else jia(t[cn].rs,cm,cx,zh+1,r);
t[cn].ge = t[t[cn].ls].ge + t[t[cn].rs].ge;
}
void bing(int cn,int cm,int l,int r)
{
if(l == r) {if(!t[cn].ls)t[cn] = t[cm]; return; }
if(!t[cn].ls)t[cn].ls = t[cm].ls;
else if(t[cm].ls)bing(t[cn].ls,t[cm].ls,l,(l+r)>>1);
if(!t[cn].rs)t[cn].rs = t[cm].rs;
else if(t[cm].rs)bing(t[cn].rs,t[cm].rs,((l+r)>>1)+1,r);
t[cn].ge = t[t[cn].ls].ge + t[t[cn].rs].ge;
}
int cha(int cn,int cm,int l,int r)
{
// printf("in cha : cn = %d cm = %d l = %d r = %d t[%d].ge = %d\n",cn,cm,l,r,cn,t[cn].ge);
if(l == r)return t[cn].ls;
int zh = (l+r)>>1;
if(t[t[cn].ls].ge >= cm)return cha(t[cn].ls,cm,l,zh);
else return cha(t[cn].rs,cm-t[t[cn].ls].ge,zh+1,r);
}
}T;
int fa[MAXN+1],da[MAXN+1];
int n,m;
int get_fa(int cn) {return fa[cn] == cn ? cn : fa[cn] = get_fa(fa[cn]); }
int main()
{
Read(n); Read(m);
for(int i = 1;i<=n;i++)fa[i] = i,da[i] = 1;
T.build(n);
for(int i = 1;i<=n;i++) {int bx; Read(bx); T.jia(T.ro[i],bx,i,1,n); }
for(int i = 1;i<=m;i++)
{
int bx,by; Read(bx); Read(by);
int lin1 = get_fa(bx), lin2 = get_fa(by);
if(lin1 == lin2)continue;
if(da[lin1] < da[lin2])swap(lin1,lin2);
fa[lin2] = lin1;
da[lin1] += da[lin2];
T.bing(T.ro[lin1],T.ro[lin2],1,n);
}
Read(m);
for(int i = 1;i<=m;i++)
{
char c; int bx,by;
while(!isalpha(c = getchar()));
Read(bx); Read(by);
if(c == 'B'){
int lin1 = get_fa(bx), lin2 = get_fa(by);
if(da[lin1] < da[lin2])swap(lin1,lin2);
if(lin1 == lin2)continue;
fa[lin2] = lin1;
da[lin1] += da[lin2];
T.bing(T.ro[lin1],T.ro[lin2],1,n);
}
else{
int lin = get_fa(bx);
if(da[lin] < by)Write(-1),puts("");
else Write(T.cha(T.ro[lin],by,1,n)),puts("");
}
}
return 0;
}