bzoj 1901 动态区间第k大 (树套树)

刚写完线段树套splay的,回头补上树状数组套主席树的;

线段树套splay的思路:
每个线段树节点上有一棵splay里面存对应区间内的所有数字;修改时直接在每个splay上进行删除节点后再添加;查询时二分值判断是第多少大的,向大逼近;


代码还是一贯的长。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int Inf = 0x3f3f3f;
const int maxn = 51000;

inline int getnum() {
	int ans = 0, f = 1;
	char c = getchar();
	if(c == '-') f = -1;
	while(c < '0' || c > '9')	c = getchar();
	while(c >= '0' && c <= '9') ans += c - '0', c = getchar();
	return ans*f;
} 

int n, m, tot;
int ch[maxn*20][2], size[maxn*20], p[maxn*20], cnt[maxn*20], key[maxn*20];
int c[maxn];

struct segment {
	int root, l, r;
	int get_ship(int x) {
		return x == ch[p[x]][1];
	} 
	void clear(int x) {
		size[x] = cnt[x] = ch[x][1] = ch[x][0] = p[x] = key[x] = 0;
	}
	void up(int x) {
		size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x];
	}
	void rotate(int x) {
		int fa = p[x], gfa = p[p[x]], ship = get_ship(x);
		ch[fa][ship] = ch[x][ship^1];	p[ch[fa][ship]] = fa;
		ch[x][ship^1] = fa;	p[fa] = x;	p[x] = gfa;
		if(gfa)
			ch[gfa][fa == ch[gfa][1]] = x;
		up(fa);  up(x);  up(gfa);
	}
	void splay(int x, int tar) {
		for(int fa; (fa = p[x]) != tar; rotate(x)) 
			if(p[fa] != tar)
				rotate((get_ship(x) == get_ship(fa))? fa:x);
/*		int fa = p[x];
		while(1) {
			if(p[fa] != tar)
				rotate((get_ship(x) == get_ship(fa))?fa:x);
			rotate(x);
			if((fa = p[x]) == tar)
				break;
		}
*/		if(tar == 0)
			root = x;
	}
	void insert(int x) {
		int now = root, fa = 0;
		if(now == 0) {
			tot++;	root = tot;	clear(tot);
			key[tot] = x;
			size[tot] = cnt[tot] = 1;
			return;
		}
		while(1) {
			if(key[now] == x) {
				cnt[now]++;
				size[now]++;	up(p[now]);
				splay(now, 0);
				return;
			}
			fa = now, now = ch[now][x > key[now]];
			if(now == 0) {
				now = ++tot;
				clear(tot);
				cnt[now] = size[now] = 1;
				key[now] = x;	p[now] = fa;	ch[fa][x > key[fa]] = now;
				up(fa);		splay(now, 0);
				return;
			}
		}
	}
	int find_no(int x) {
		int now = root;
		while(1) {
			if(x <= size[ch[now][0]])
				now = ch[now][0];
			else {
				x -= size[ch[now][0]] + cnt[now];
				if(x <= 0)
					return key[now];
				now = ch[now][1];
			}
		}
	}//找到第x大的数字 
	int find_num(int x) {
		int now = root, ans = 0;
		while(1) {
			if(now == 0)
				return ans;
			if(x < key[now])
				now = ch[now][0];
			else {
				ans += (ch[now][0]?size[ch[now][0]]:0);
				if(x == key[now]) {
					splay(now, 0);
					return ans;
				}
				ans += cnt[now];
				now = ch[now][1];
			}
		}
	}//找数字是第多少大 
	int pre() {
		int now = ch[root][0];
		while(ch[now][1])
			now = ch[now][1];
		return now;
	}
	int next() {
		int now = ch[now][1];
		while(ch[now][0])
			now = ch[now][0];
		return now;
	}
	void delate(int x) {
		int no = find_num(x);
		if(cnt[root] > 1) {
			cnt[root]--; size[root]--;
			return;
		}
		if(!ch[root][0] && !ch[root][1]) {
			clear(root);	root = 0;
			return;
		}
		if(!ch[root][0]) {
			int oldroot = root;
			root = ch[root][1];	p[root] = 0;
			clear(oldroot);
			return;
		}
		if(!ch[root][1]) {
			int oldroot = root;
			root = ch[root][0];	p[root] = 0;
			clear(oldroot);
			return;
		}
		int bigleft = pre(), oldroot = root;
		splay(bigleft, 0);
		ch[root][1] = ch[oldroot][1];
		p[ch[root][1]] = root; clear(oldroot);
		up(root);
		return;
	}
}a[maxn*4];

void build(int o, int l, int r) {
	a[o].l = l, a[o].r = r;
	for(int i = l; i <= r; i++) 
		a[o].insert(c[i]);
	if(l == r)
		return;
	int mid = l + (r-l)/2;
	build(o*2, l, mid);		build(o*2+1, mid+1, r);
}

void update(int o, int l, int k) {
	a[o].delate(c[l]);
	a[o].insert(k);
	if(a[o].l == a[o].r)	return;
	int mid = a[o].l + (a[o].r-a[o].l)/2;
	if(mid >= l)
		update(o*2, l, k);
	else
		update(o*2+1, l, k); 
}//将第l个数字替换为k; 

int sum = 0;

void find_rank(int o, int l, int r, int k) {
	if(a[o].l >= l && a[o].r <= r) {
		sum += a[o].find_num(k);
		return;
	} 
	int mid = a[o].l + (a[o].r - a[o].l)/2;
	if(mid >= l)
		find_rank(o*2, l, r, k);
	if(mid < r)
		find_rank(o*2+1, l, r, k);
}//找到k这个数字在l到r中排名为多少; 

void rank_k(int l, int r, int k) {
	int ml = 0, mr = 6, mid, ans = 0;
	while(ml < mr) {
		mid = mr - (mr-ml)/2;
		sum = 1;
		find_rank(1, l, r, mid);
		if(sum > k)
			mr = mid-1;
		else
			ml = mid;
	}
	printf("%d\n", mr);
}

int main() {
	int T;
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++)
			scanf("%d", &c[i]);
		build(1, 1, n);
		for(int i = 1; i <= m; i++) {
			getchar();
			char s;
			scanf("%c", &s);
			int l, r, k, x;
			if(s == 'Q') {
				scanf("%d%d%d", &l, &r, &k);
				rank_k(l, r, k);
			}
			if(s == 'C') {
				scanf("%d%d", &x, &k);
				update(1, x, k);
			}
		}
		tot = 0;
		memset(a, 0, sizeof(a));
	}
	return 0;
}

发布了44 篇原创文章 · 获赞 5 · 访问量 2万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览