zoukankan      html  css  js  c++  java
  • 树套树

    树状数组套平衡树

    可以解决区间的可减问题, 比如区间有多少个大于等于/小于等于 k 的数。

    对于 [这道题], 其实也可以用树状数组套平衡树, 不过要外层二分多 log, 平衡树比较难多树二分。

    具体地, 对于修改, 拆成插入删除;对于排名直接做;对于前驱后继 kth, 都可以用外层套二分做。

    整体的复杂度, 空间是 O(q log n), 时间是 O(q log w log2 n) 的。

    具体说说前驱后继二分的细节, 对于一个数 x 的前驱, 是 [0,x-1] 中最大的和不同的 x “大于等于其的有多少” 的数, 后继也差不多, 比较糊地说一下就是 x 往后找后第一个和 x 的 “小于等于其的有多少” 的数值不同的数。

    #include<bits/stdc++.h>
    #define INF 1e9
    using namespace std;
    
    const int N = 5e4 + 3, SZ = (5e4+23) * 2 * 16 + 2333;
    
    int cnt;
    #define newnode(s, v, a, b) (&(*pool[cnt++] = node(s, v, a, b)))
    #define merge(a, b) newnode(a->siz+b->siz, b->val, a, b)
    #define upd(me) if(me->ls->siz) me->siz=me->ls->siz+me->rs->siz, me->val=me->rs->val
    struct node{
    	int siz, val;
    	node *ls, *rs;
    	node(int s, int v, node *a, node *b) : siz(s), val(v), ls(a), rs(b) {
    	}
    	node() {
    	}
    } *root[N], *pool[SZ], t[SZ], *emp;
    
    inline void maintain(node *me) {
    	if(me->ls->siz > me->rs->siz * 4)
    		me->rs = merge(me->ls->rs, me->rs), pool[--cnt] = me->ls, me->ls = me->ls->ls;
    	if(me->rs->siz > me->ls->siz * 4)
    		me->ls = merge(me->ls, me->rs->ls), pool[--cnt] = me->rs, me->rs = me->rs->rs;
    }
    
    void ins(int x, node *me) {
    	if(me->siz == 1)
    		me->ls = newnode(1, min(x, me->val), emp, emp),
    		me->rs = newnode(1, max(x, me->val), emp, emp);
    	else
    		ins(x, x>me->ls->val ? me->rs : me->ls), maintain(me);
    	upd(me);
    }
    
    void del(int x, node *me) {
    	if(me->ls->siz == 1 && me->ls->val == x)
    		pool[--cnt] = me->ls, pool[--cnt] = me->rs, *me = *me->rs;
    	else if(me->rs->siz == 1 && me->rs->val == x)
    		pool[--cnt] = me->ls, pool[--cnt] = me->rs, *me = *me->ls;
    	else
    		del(x, x>me->ls->val ? me->rs : me->ls), maintain(me);
    	upd(me);
    }
    
    int low(int x, node *me) {
    	if(me->siz == 1) return (me->val <= x);
    	else return x>=me->ls->val ? me->ls->siz + low(x, me->rs) : low(x, me->ls);
    } 
    
    int upp(int x, node *me) {
    	return me->siz - low(x-1, me);
    }
    
    int n, m, a[N];
    
    void ins(int x, int v) {
    	for(; x<=n; x += (x&(-x))) ins(v, root[x]);
    }
    void del(int x, int v) {
    	for(; x<=n; x += (x&(-x))) del(v, root[x]);
    }
    int low(int x, int v) {
    	int res = 0;
    	for(; x; x-=(x&(-x))) res += low(v, root[x]);
    	return res;
    }
    int low(int l, int r, int v) {
    	return low(r, v) - low(l-1, v);
    }
    int upp(int x, int v) {
    	int res = 0;
    	for(; x; x-=(x&(-x))) res += upp(v, root[x]);
    	return res;
    }
    int upp(int l, int r, int v) {
    	return upp(r, v) - upp(l-1, v);
    }
    
    int main() {
    	emp = new node(0, 0, NULL, NULL);
    	for(int i=0; i<SZ; ++i) pool[i] = &t[i];
    	scanf("%d%d", &n, &m);
    	for(int i=1; i<=n; ++i) root[i] = newnode(1, INF, emp, emp);
    	for(int i=1; i<=n; ++i) scanf("%d", &a[i]), ins(i, a[i]);
    	while(m--)
    	{
    		int opt;
    		scanf("%d", &opt);
    		if(opt == 1)
    		{
    			int l, r, k;
    			scanf("%d%d%d", &l, &r, &k);
    			cout << low(l, r, k-1) + 1 << '
    ';
    		}
    		if(opt == 2)
    		{
    			int l, r, k;
    			scanf("%d%d%d", &l, &r, &k);
    			int L=0, R=100000000;
    			while(L != R)
    			{
    				int mid = (L+R+1) >> 1;
    				if(low(l, r, mid-1) + 1 <= k) L = mid;
    				else R = mid-1;
    			}
    			cout << L << '
    ';
    		}
    		if(opt == 3)
    		{
    			int pos, k;
    			scanf("%d%d", &pos, &k);
    			del(pos, a[pos]);
    			ins(pos, (a[pos] = k));
    		}
    		if(opt == 4)
    		{
    			int l, r, k;
    			scanf("%d%d%d", &l, &r, &k);
    			if(k == 0) {
    				puts("-2147483647");
    				continue;
    			}
    			int L=-1, R=k-1, tmp = upp(l, r, k);
    			while(L!=R) {
    				int mid = (L+R+1) >> 1;
    				if(upp(l, r, mid) != tmp) L = mid;
    				else R = mid-1;
    			}
    			
    			if(L == -1) puts("-2147483647");
    			else cout << L << '
    ';
    		}
    		if(opt == 5)
    		{
    			int l, r, k;
    			scanf("%d%d%d", &l, &r, &k);
    			if(k == 100000000) {
    				puts("2147483647");
    				continue;
    			}
    			int L=k+1, R=100000000+1, tmp = low(l, r, k);
    			while(L!=R) {
    				int mid = (L+R) >> 1;
    				if(low(l, r, mid) != tmp) R = mid;
    				else L = mid+1;
    			}
    			
    			if(L == 100000001) puts("2147483647");
    			else cout << L << '
    ';
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    [后缀数组] Luogu P5028 Annihilate
    [后缀数组] Luogu P3809 后缀排序
    [差分][线段树] Luogu P4243 等差数列
    [线段树] Luogu P4314 COU监控
    [二分][dp凸优化] Luogu P4383 林克卡特树lct
    [树上差分][dfs] Luogu P4652 One-Way Streets
    [dfs][思维] Luogu P3208 矩阵
    [dfs][二进制状压] Luogu P4906 小奔关闹钟
    [容斥] Luogu P5339 唱、跳、rap和篮球
    [dfs][模拟网络流] Luogu P4189 星际旅行
  • 原文地址:https://www.cnblogs.com/tztqwq/p/14307089.html
Copyright © 2011-2022 走看看