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

来源:互联网 发布:网络消费者服务热线 编辑:程序博客网 时间:2024/06/02 09:39
刚写完线段树套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);elseupdate(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;elseml = 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;}

0 0
原创粉丝点击