n个数,q个询问 (n<=50000, q<=10000)
Q x y z 代表询问[x, y]区间里的第z小的数
C x y 代表将(从左往右数)第x个数变成y
先建立一颗静态的主席树,将初始值依次插入并建树。所谓静态指:可以查询,不在这颗树上进行修改操作。
对于修改操作,另外建一批主席树,每个树管理一个位置(即1-n)的权值信息,1-n位置的权值初始都为0,因此可以把这批主席树的根都初始化为root[0]。
对于静态主席树来说,建树过程中,Tree[i]是在Tree[i-1]的基础上继承并发展而来的;而第二批主席树则不然,它每棵树只保存一个位置的权值信息,i位置的权值并不叠加i-1位置的权值,他们是独立的,所以当需要区间统计的时候,就需要我们手工求和,区间求和,自然想到树状数组。
根据树状数组,当要更新i位置时,也更新i+lowbit(i)…到n,当要查询1-i的前缀和就查i, i-lowbit(i)…到1即可。
对于这两批主席树,可以做一个比喻帮助理解:假设我现在给你一个数,要求在多次加或减运算后求出结果,可以这样做就(当然不用这么麻烦,只是配合举例):开一个变量x把给的那个数作为初始值先存下来,再来一个变量y记录每次的+或-值,记录变化,最后只需x+y就算出来了。这里的x就相当于第一批静态主席树,y就相当于第二批记录修改的主席树。
还不懂可以看看:传送,不过我觉得他后面有的图画的有点迷。
#include
#include
#include
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 60000+50;
const int maxq = 10000+50;
int n, m;
struct query{
int a,b,c;
}Q[maxq];
int lson[maxn<<5], rson[maxn<<5], Tree[maxn<<5], root[maxn];
int S[maxn];
int use[maxn]; // use[x]: 记录在x位置的线段树现在该从哪个节点了向下查了,层层记录,逐步下移
int A[maxn], Hash[maxn]; // 原数组和哈希数组
int cnt;
void Build(int &rt, int l, int r){
rt = ++cnt; Tree[rt] = 0;
if(l == r) return;
int mid = (l+r) >> 1;
Build(lson[rt], l, mid);
Build(rson[rt], mid+1, r);
}
void update(int& new_rt, int old_rt, int l, int r, int pos, int val){
new_rt = ++cnt;
lson[new_rt] = lson[old_rt]; rson[new_rt] = rson[old_rt];
Tree[new_rt] = Tree[old_rt] + val;
if(l == r) return;
int mid = (l+r) >> 1;
if(pos <= mid) update(lson[new_rt], lson[old_rt], l, mid, pos, val);
else update(rson[new_rt], rson[old_rt], mid+1, r, pos, val);
}
inline int lowbit(int x){return x & (-x);}
int sum(int x){
int ans = 0;
while(x > 0){
ans += Tree[lson[use[x]]];
x -= lowbit(x);
}
return ans;
}
int Query(int la, int rb, int rta, int rtb, int l, int r, int k){
if(l == r) return l;
int mid = (l+r) >> 1;
int leftot = Tree[lson[rtb]] - Tree[lson[rta]] + sum(rb) - sum(la); // 静态主席树+树状数组 统计出左边一共有多少数
if(k <= leftot){ // kth 在左边
for(int i = la; i > 0; i-=lowbit(i)) use[i] = lson[use[i]];
for(int i = rb; i > 0; i-=lowbit(i)) use[i] = lson[use[i]];
return Query(la, rb, lson[rta], lson[rtb], l, mid, k);
}
else{
for(int i = la; i > 0; i-=lowbit(i)) use[i] = rson[use[i]];
for(int i = rb; i > 0; i-=lowbit(i)) use[i] = rson[use[i]];
return Query(la, rb, rson[rta], rson[rtb], mid+1, r, k-leftot);
}
}
void add(int x, int pos, int val){
while(x <= n){
update(S[x], S[x], 1, m, pos, val);
x += lowbit(x);
}
}
int main()
{
//freopen("E:/Cgit/Practice/ACM/in.txt","r",stdin);
int T, q; scanf("%d",&T);
char ch[5];
while(T--){
scanf("%d%d",&n,&q);
m = 0;
for(int i = 1; i <= n; ++i) scanf("%d", &A[i]), Hash[++m] = A[i];
for(int i = 1; i <= q; ++i){
scanf("%s", ch);
scanf("%d%d", &Q[i].a, &Q[i].b);
if(ch[0] == 'C'){
Hash[++m] = Q[i].b;
Q[i].c = -1;
}
else{
scanf("%d", &Q[i].c);
}
}
// 离散化
sort(Hash+1, Hash+m+1);
int tmp = 1;
for(int i = 2; i <= m; ++i) if(Hash[i] != Hash[i-1]) Hash[++tmp] = Hash[i];
m = tmp;
//printf("m = %d\n", m);
// 建静态主席树
cnt = 0;
Build(root[0], 1, m);
// 插入初始值
for(int i = 1; i <= n; ++i){
int pos = lower_bound(Hash+1, Hash+m+1, A[i]) - Hash;
update(root[i], root[i-1], 1, m, pos, 1);
}
//for(int i = 1; i <= 30; ++i) printf("%d ", root[i]);
// 另外建n颗主席树
for(int i = 1; i <= n; ++i) S[i] = root[0]; // 初始化
for(int i = 1; i <= q; ++i){
int a = Q[i].a, b = Q[i].b, c = Q[i].c;
if(c == -1){ // change a b
int pos1 = lower_bound(Hash+1, Hash+m+1, A[a]) - Hash;
int pos2 = lower_bound(Hash+1, Hash+m+1, b) - Hash;
add(a, pos1, -1);
add(a, pos2, 1);
A[a] = b;
}
else{ // query a b c
for(int j = a-1; j > 0; j-= lowbit(j))
use[j] = S[j];
for(int j = b; j > 0; j-= lowbit(j))
use[j] = S[j];
int pos = Query(a-1, b, root[a-1], root[b], 1, m, c);
printf("%d\n", Hash[pos]);
}
}
}
fclose(stdin);
return 0;
}