其他分享
首页 > 其他分享> > P3380 【模板】二逼平衡树(树套树)

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

作者:互联网

传送门

分析

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

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

我们对树状数组和主席树进行组装,树状数组的每一个节点都表示着主席树的某一系列版本
对于要查询某个区间的版本情况,找到需要的左端点版本对应的一系列树状数组维护的版本 l − 1 l-1 l−1,以及右端点版本 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;
}

标签:qr,树套,int,uniarr,tr,son,unicnt,P3380,模板
来源: https://blog.csdn.net/qq_45754027/article/details/120293108