[ZOJ 2112]Dynamic Rankings

树套树


外层线段树, 内层平衡树

每次查询的时候需要把区间分解,然后在这些平衡树上二分

注意size域的维护。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200001
#define M 1300001
using namespace std;

int T, n, m;
int a[50001], root[N];
int sz, c[M][2], size[M], val[M], w[M], rnd[M];

void update(int k){
	size[k] = size[c[k][0]] + size[c[k][1]] + w[k];
}

void rotate(int &o, int mark){
	int cur = c[o][mark];
	c[o][mark] = c[cur][mark ^ 1];
	c[cur][mark ^ 1] = o;
	size[cur] = size[o];
	update(o);
	o = cur;
}

int Newnode(int num){
	int now = ++ sz;
	size[now] = w[now] = 1;
	c[now][0] = c[now][1] = 0;
	rnd[now] = rand();
	val[now] = num;
	return now;
}

void Insert(int &k, int num){
	if(k == 0){
		k = Newnode(num);
		return;
	}
	size[k] ++;
	if(val[k] == num){
		w[k] ++;
		return;
	}

	if(num < val[k]){
		Insert(c[k][0], num);
		if(rnd[c[k][0]] < rnd[k])
		    rotate(k, 0);
	}
	else{
		Insert(c[k][1], num);
        if(rnd[c[k][1]] < rnd[k])
		    rotate(k, 1);
	}
}

void Delete(int &k, int num){
	if(val[k] == num){
		if(w[k] > 1){
			w[k] --;
			size[k] --;
			return;
		}
		if(c[k][0] == 0 || c[k][1] == 0)
			k = c[k][0] + c[k][1];
		else if(rnd[c[k][0]] < rnd[c[k][1]]){
			rotate(k, 0);
			Delete(k, num);
		}
		else{
			rotate(k, 1);
            Delete(k, num);
		}
		return;
	}
	Delete(c[k][num > val[k]], num);
	size[k] --;
}

int TMP;

void build(int id, int l, int r, int x, int num){
	Insert(root[id], num);
	if(l == r)return;
	int mid = l + r >> 1;
	if(x <= mid)build(id << 1, l, mid, x, num);
	else build(id << 1 | 1, mid + 1, r, x, num);
}

void change(int id, int l, int r, int x, int num, int y){
	Delete(root[id], y);
	Insert(root[id], num);
	if(l == r)return;
	int mid = l + r >> 1;
	if(x <= mid)change(id << 1, l, mid, x, num, y);
	else change(id << 1 | 1, mid + 1, r, x, num, y);
}

void find(int id, int num){
	if(id == 0)return;
	if(val[id] <= num){
		TMP += size[c[id][0]] + w[id];
		find(c[id][1], num);
	}
	else find(c[id][0], num);
}

void Qry(int id, int l, int r, int x, int y, int num){
	if(l == x && r == y){
		find(root[id], num);
		return;
	}
	int mid = l + r >> 1;
	if(mid >= y)Qry(id << 1, l, mid, x, y, num);
	else if(mid < x)Qry(id << 1 | 1, mid + 1, r, x, y, num);
	else{
		Qry(id << 1, l, mid, x, mid, num);
		Qry(id << 1 | 1, mid + 1, r, mid + 1, y, num);
	}
}

int main(){
	int test;
	int x, y, z;
	scanf("%d", &test);
	while(test --){
		memset(root, 0, sizeof root);
		sz = 0;
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i ++)
		    scanf("%d", &a[i]);
		for(int i = 1; i <= n; i ++)
		    build(1, 1, n, i, a[i]);
		for(int i = 1; i <= m; i ++){
			char QAQ = getchar();
			for(; QAQ < '!'; QAQ = getchar());
			if(QAQ == 'C'){
				scanf("%d%d", &x, &y);
				change(1, 1, n, x, y, a[x]);
				a[x] = y;
			}
			else{
				scanf("%d%d%d", &x, &y, &z);
				int l = 0, r = 1000000000;
				while(l < r){
					int mid = l + r >> 1;
					TMP = 0;
					Qry(1, 1, n, x, y, mid);
					if(TMP >= z)r = mid;
					else l = mid + 1;
					
				}
				printf("%d\n", r);
			}
		}
	}

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值