P3380 【模板】二逼平衡树(树套树)

传送门

  • 查询 k k k 在区间内的排名
  • 查询区间内排名为 k k k的值
  • 修改某一位值上的数值
  • 查询 k k k 在区间内的前驱(前驱定义为严格小于 x x x,且最大的数,若不存在输出 − 2147483647 -2147483647 2147483647
  • 查询 k k k 在区间内的后继(后继定义为严格大于 x x x,且最小的数,若不存在输出 2147483647 2147483647 2147483647

分析

对于静态区间第K大来说,那就上主席树吧
如果动态的话,就树套树了
如果是双动态的话,那就树套树套树了(大雾)
此时我们只需要实现三个功能即可

  • 根据值查询排名
  • 根据排名查值
  • 修改值

对于一棵静态区间第K大的主席树来说,维护的是一个前缀和
一旦某一个版本(位置)的值改变了,那么必须重构这个版本(位置)往后的所有版本状态
此时修改复杂度最差为 n ∗ l o g ( n ) n*log(n) nlog(n),我不能接受
那么什么能够在短时间维护一个前缀和的修改呢?
我们想到了树状数组,每次 l o g ( n ) log(n) log(n)时间修改一个位置
查询也是 l o g ( n ) log(n) log(n)

我们对树状数组和主席树进行组装,树状数组的每一个节点都表示着主席树的某一系列版本
对于要查询某个区间的版本情况,找到需要的左端点版本对应的一系列树状数组维护的版本 l − 1 l-1 l1,以及右端点版本 r r r(共有将近 l o g ( n ) log(n) log(n)个版本,使用 lowbit 预处理出这一系列版本)
在这两个主席树系列版本上进行差分

修改值也是先预处理出这一系列版本,在这 l o g ( n ) log(n) log(n)个版本中更新

对于第三第四个操作
通过第一个和第二个操作的组合,就能实现

代码

//P3380 
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("P3380_1.in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;

int arr[MAX_N];
int uniarr[MAX_N];
int unicnt = 0;

struct Qr {
	int opt, l, r, k;
}qr[MAX_N];

struct Tr {
	int k, l, r;
}tr[MAX_N*25];

int indx = 0;
int root[MAX_N];

void update(int&rt, int l, int r, int x, int k) {
	if (!rt) rt = ++indx;
	tr[rt].k += k;
	if (l == r) {
		return;
	}
	int mid = l + ((r-l)>>1);
	if (x <= mid) update(tr[rt].l, l, mid, x, k);
	else update(tr[rt].r, mid+1, r, x, k);
}

int query(int rt, int l, int r, int x, int y) {
	if (!rt) return 0;
	if (x <= l && r <= y) {
		return tr[rt].k;
	}
	int mid = l + ((r-l)>>1);
	if (y <= mid) return query(tr[rt].l, l, mid, x, y);
	if (x  > mid) return query(tr[rt].r, mid+1, r, x, y);
	return query(tr[rt].l, l, mid, x, y) + query(tr[rt].r, mid+1, r, x, y);
}

int cnt[2];
int son[2][MAX_N];

int kth(int l, int r, int k) {
	if (l == r) {
		return l;
	}
	int mid = l + ((r-l)>>1);
	int lsum = 0, sum = 0;
	for (int i = 1; i <= cnt[1]; ++i) {
		sum += tr[son[1][i]].k;
		lsum += tr[tr[son[1][i]].l].k;
	}
	for (int i = 1; i <= cnt[0]; ++i) {
		sum -= tr[son[0][i]].k;
		lsum -= tr[tr[son[0][i]].l].k;
	}
	if (lsum >= k) {
		for (int i = 1; i <= cnt[0]; ++i) {
			son[0][i] = tr[son[0][i]].l;
		}
		for (int i = 1; i <= cnt[1]; ++i) {
			son[1][i] = tr[son[1][i]].l;
		}
		return kth(l, mid, k);
	} else {
		for (int i = 1; i <= cnt[0]; ++i) {
			son[0][i] = tr[son[0][i]].r;
		}
		for (int i = 1; i <= cnt[1]; ++i) {
			son[1][i] = tr[son[1][i]].r;
		}
		return kth(mid+1, r, k - lsum);
	}
}

int rak(int x) {
	int sum = 0;
	int t;
	for (int i = 1; i <= cnt[1]; ++i) {
		t = query(son[1][i], 1, unicnt, 1, x);
		sum += t;
	}
	for (int i = 1; i <= cnt[0]; ++i) {
		t = query(son[0][i], 1, unicnt, 1, x);
		sum -= t;
	}
	return sum;
}

inline int lowbit(int x) {
	return x&-x;
}

void modify(int x, int k, int w) {
	while (x <= N) {
		update(root[x], 1, unicnt, k, w);
		x += lowbit(x);
	}
}

void init(int l, int r) {
	memset(cnt, 0, sizeof cnt);
	--l;
	while (l) {
		son[0][++cnt[0]] = root[l];
		l -= lowbit(l);
	}
	while (r) {
		son[1][++cnt[1]] = root[r];
		r -= lowbit(r);
	}
}

/**
9 1
4 2 2 1 9 4 0 1 1
2 1 4 3
**/
void solve(){
	sc("%lld%lld", &N, &M);
	uniarr[++unicnt] = -2147483647;
	uniarr[++unicnt] = 2147483647;
	for (int i = 1; i <= N; ++i) {
		sc("%lld", &arr[i]);
		uniarr[++unicnt] = arr[i];
	}
	
	int opt, l, r, k;
	for (int i = 1; i <= M; ++i) {
		sc("%lld%lld%lld", &qr[i].opt, &qr[i].l, &qr[i].r);
		if (qr[i].opt == 1) {
			sc("%lld", &qr[i].k);
			uniarr[++unicnt] = qr[i].k;
		} else if (qr[i].opt == 2) {
			sc("%lld", &qr[i].k);
		} else if (qr[i].opt == 3) {
			qr[i].k = qr[i].r;
			uniarr[++unicnt] = qr[i].k;
		} else if (qr[i].opt == 4) {
			sc("%lld", &qr[i].k);
			uniarr[++unicnt] = qr[i].k;
		} else {
			sc("%lld", &qr[i].k);
			uniarr[++unicnt] = qr[i].k;
		}
	}
	
	sort(uniarr+1, uniarr+1+unicnt);
	unicnt = unique(uniarr+1, uniarr+1+unicnt) - uniarr - 1;
	
	
	for (int i = 1; i <= N; ++i) {
		arr[i] = lower_bound(uniarr+1, uniarr+1+unicnt, arr[i]) - uniarr;
		modify(i, arr[i], 1);
	}
	for (int i = 1; i <= M; ++i) {
		if (qr[i].opt != 2) qr[i].k = lower_bound(uniarr+1, uniarr+1+unicnt, qr[i].k) - uniarr;
		if (qr[i].opt == 1) {
			init(qr[i].l, qr[i].r);
			pr("%lld\n", rak(qr[i].k-1) + 1);
		} else if (qr[i].opt == 2) {
			init(qr[i].l, qr[i].r);
			pr("%lld\n", uniarr[kth(1, unicnt, qr[i].k)]);
		} else if (qr[i].opt == 3) {
			modify(qr[i].l, arr[qr[i].l], -1);
			modify(qr[i].l, arr[qr[i].l] = qr[i].k, 1);
		} else if (qr[i].opt == 4) {
			init(qr[i].l, qr[i].r);
			int rk = rak(qr[i].k-1);
			init(qr[i].l, qr[i].r);
			pr("%lld\n", uniarr[kth(1, unicnt, rk)]);
		} else {
			init(qr[i].l, qr[i].r);
			int rk = rak(qr[i].k);
			init(qr[i].l, qr[i].r);
			pr("%lld\n", uniarr[kth(1, unicnt, rk+1)]);
		}
	}
	
}

signed main()
{
	#ifndef ONLINE_JUDGE
	FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值