题目:
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112
题意:
给出一个长度为n的整数数组,有以下两种操作: Q i j k意为求区间[i,j]内的第k小数,C i t意为把第i个数换成t
思路:
一直觉得树套树高大上,今天学着写了一波。。。建立一颗线段树,线段树中每个节点再对应一棵平衡树,用来维护节点所管辖的区间,用平衡树可以在log(n)的时间内确定一个值的名次。于是可以二分枚举第k大值,然后在去线段树中查询这个值的名次,就可以了,大致就是这样,平衡树用的是treap
#include <bits/stdc++.h>
const int N = 50000 + 10, INF = 0x3f3f3f3f;
int tot;
int a[N];
struct seg_node
{
int l, r, root;
}g[N*4];
struct treap_node
{
int val, pri, son[2];
int sz, num;
void init(int _val, int _pri, int _sz, int _num)
{
val = _val, pri = _pri, sz = _sz, num = _num;
son[0] = son[1] = 0;
}
}tr[N*20];
void treap_init()
{
tot = 0;
tr[0].init(0, 0, 0, 0);
}
void treap_update(int x)
{
tr[x].sz = tr[tr[x].son[0]].sz + tr[tr[x].son[1]].sz + tr[x].num;
}
void treap_rotate(int &x, int p)
{
int y = tr[x].son[!p];
tr[x].son[!p] = tr[y].son[p];
tr[y].son[p] = x;
treap_update(x), treap_update(y);
x = y;
}
void treap_insert(int &x, int val)
{
if(! x) tr[x = ++tot].init(val, rand(), 1, 1);
else
{
tr[x].sz++;
if(tr[x].val == val) tr[x].num++;
else
{
int p = val > tr[x].val;
treap_insert(tr[x].son[p], val);
if(tr[x].pri < tr[tr[x].son[p]].pri) treap_rotate(x, !p);
}
}
}
void treap_delete_node(int &x, int val)
{
if(tr[x].val == val)
{
if(tr[x].num > 1) tr[x].sz--, tr[x].num--;
else if(tr[x].son[0] && tr[x].son[1])
{
int p = tr[tr[x].son[0]].pri > tr[tr[x].son[1]].pri;
treap_rotate(x, p);
treap_delete_node(x, val);
}
else x = tr[x].son[0] + tr[x].son[1];
}
else
{
tr[x].sz--;
int p = val > tr[x].val;
treap_delete_node(tr[x].son[p], val);
}
}
int treap_get_rank(int x, int val)
{
if(! x) return 0;
if(val > tr[x].val) return tr[x].num + tr[tr[x].son[0]].sz + treap_get_rank(tr[x].son[1], val);
else if(val < tr[x].val) return treap_get_rank(tr[x].son[0], val);
else return tr[tr[x].son[0]].sz + tr[x].num;
}
void seg_build(int l, int r, int k)
{
g[k].l = l, g[k].r = r, g[k].root = 0;
//treap_insert(g[k].root, a[l]);
if(l == r) return;
int mid = (l + r) >> 1;
seg_build(l, mid, k << 1);
seg_build(mid + 1, r, k << 1|1);
}
void seg_init(int x, int k, int val)
{
treap_insert(g[k].root, val);
if(g[k].l == g[k].r) return;
int mid = (g[k].l + g[k].r) >> 1;
if(x <= mid) seg_init(x, k << 1, val);
else seg_init(x, k << 1|1, val);
}
void seg_update(int x, int tval, int val, int k)
{
treap_delete_node(g[k].root, tval);
treap_insert(g[k].root, val);
if(g[k].l == g[k].r) return;
int mid = (g[k].l + g[k].r) >> 1;
if(x <= mid) seg_update(x, tval, val, k << 1);
else seg_update(x, tval, val, k << 1|1);
}
int seg_query(int l, int r, int val, int k)
{
if(l <= g[k].l && g[k].r <= r)
{
return treap_get_rank(g[k].root, val);
}
int mid = (g[k].l + g[k].r) >> 1;
int ans = 0;
if(l <= mid) ans += seg_query(l, r, val, k << 1);
if(r > mid) ans += seg_query(l, r, val, k << 1|1);
return ans;
}
int main()
{
int t, n, m;
scanf("%d", &t);
while(t--)
{
treap_init();
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
seg_build(1, n, 1);
for(int i = 1; i <= n; i++) seg_init(i, 1, a[i]);//把所有值依次插入到线段树对应节点的平衡树中
char ch;
int x, y, z;
for(int i = 1; i <= m; i++)
{
scanf(" %c", &ch);
if(ch == 'Q')
{
scanf("%d%d%d", &x, &y, &z);
int l = -1e9, r = 1e9, res;
while(l <= r)
{
int mid = (l + r) >> 1;
int k = seg_query(x, y, mid, 1);
if(k >= z) res = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d\n", res);
}
else
{
scanf("%d%d", &x, &y);
seg_update(x, a[x], y, 1);
a[x] = y;
}
}
}
return 0;
}