主席树 && 带修主席树 (模板 && 代码解释)

 

先贴模板 后代码解释

线上U盘,哈哈哈哈

静态主席树:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <cmath>
#include <map>
#include <queue>
#include <algorithm>
#include <set>
#include <vector>
#include <stack>
#define Clear( x , y ) memset( x , y , sizeof(x) );
#define Qcin() std::ios::sync_with_stdio(false);
using namespace std;
typedef long long LL;
const int Maxn = 1e5 + 7;
const int Inf = 1e9 + 7;
int N , M;
int cnt = 0, root[Maxn] , a[Maxn];
struct node{
	int l , r , sum;
}T[Maxn*40];
vector <int> v;
void init(){
	cnt = 0;
	T[cnt].l=0, T[cnt].r=0 , T[cnt].sum=0;
	root[cnt] = 0;
	v.clear();
}
int getid(int x){
	return lower_bound(v.begin(),v.end(),x) - v.begin() + 1;
}
void updata(int l,int r, int &x , int y , int pos){
	T[++cnt]=T[y] , T[cnt].sum++ , x=cnt;
	if(l==r)	return;
	int mid = (l+r) /2;
	if(mid >= pos)	updata(l,mid , T[x].l , T[y].l , pos);
	else	updata(mid+1,r , T[x].r , T[y].r , pos);
}
int query(int l,int r , int x , int y , int k){
	if(l==r)	return l;
	int mid = (l+r) /2;
	int sum = T[T[y].l].sum - T[T[x].l].sum;
	if(sum >= k)	return query(l,mid, T[x].l , T[y].l , k);
	else return query(mid+1,r , T[x].r , T[y].r , k-sum);
}
int main()
{
	init();
	scanf(" %d %d",&N,&M);
	for(int i=1 ; i <= N ; i++){
		scanf(" %d",&a[i]);
		v.push_back(a[i]);
	}
	sort(v.begin() , v.end());
	v.erase(unique(v.begin() , v.end()) , v.end());
	for(int i=1;i<=N;i++){
		updata(1,N , root[i] , root[i-1] , getid(a[i]));
	}
	while(M--){
		int x , y , k;
		scanf(" %d %d %d",&x,&y,&k);
		int ans = v[query(1,N, root[x-1] , root[y] , k)-1];
		printf("%d\n",ans);
	}
	return 0;
}

 

 

带修主席树

模板:

struct Node {
	int l, r, k, f;
}q[MaxQ];

int root[MaxN], lson[MaxN * 40], rson[MaxN * 40], sum[MaxN * 40];
int n, m, tot, cnt, idx;
int a[MaxN], cpy[MaxN];
int S[MaxN], use[MaxN];

int build(int l, int r) {
	int rt = tot++; sum[rt] = 0;
	if(l != r) {
		int mid = (l + r) >> 1;
		lson[rt] = build(l, mid);
		rson[rt] = build(mid + 1, r);
	}
	return rt;
}

int update(int last, int pos, int val) {
	int rt = tot++, tmp = rt, l = 0, r = cnt - 1;
	sum[rt] = sum[last] + val;
	while(l < r) {
		int mid = (l + r) >> 1;
		if(pos <= mid) {
			lson[rt] = tot++; rson[rt] = rson[last];
			rt = lson[rt]; last = lson[last];
			r = mid;
		}
		else {
			rson[rt] = tot++; lson[rt] = lson[last];
			rt = rson[rt]; last = rson[last];
			l = mid + 1;
		}
		sum[rt] = sum[last] + val;
	}
	return tmp;
}

int lowbit(int x) { return x & (-x); }
void add(int p, int pos, int val) {
	for(int i = p; i <= n; i += lowbit(i)) S[i] = update(S[i], pos, val);
}
int getSum(int p) {
	int ans = 0;
	for(int i = p; i > 0; i -= lowbit(i)) ans += sum[lson[use[i]]];
	return ans;
}

int query(int L, int R, int k) {
	int l_root = root[L-1], r_root = root[R];
	int l = 0, r = cnt-1;
	for(int i = L - 1; i > 0; i -= lowbit(i)) use[i] = S[i];
	for(int i = R; i > 0; i -= lowbit(i)) use[i] = S[i];
	while(l < r) {
		int mid = (l + r) >> 1;
		int tmp = getSum(R) - getSum(L-1) + sum[lson[r_root]] - sum[lson[l_root]];
		if(tmp >= k) {
			r = mid;
			for(int i = L-1; i > 0; i -= lowbit(i)) use[i] = lson[use[i]];
			for(int i = R; i > 0; i -= lowbit(i)) use[i] = lson[use[i]];
			l_root = lson[l_root], r_root = lson[r_root];
		}
		else {
			l = mid + 1, k -= tmp;
			for(int i = L - 1; i > 0; i -= lowbit(i)) use[i] = rson[use[i]];
			for(int i = R; i > 0; i -= lowbit(i)) use[i] = rson[use[i]];
			l_root = rson[l_root], r_root = rson[r_root];
		}
	}
	return l;
}

int getId(int x) { return lower_bound(cpy, cpy + cnt, x) - cpy; }

int main() {
	tot = cnt = idx = 0;
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]); 
		cpy[idx++] = a[i];
	}
	for(int i = 0; i < m; i++) {
		char op[10]; scanf("%s", op);
		if(op[0] == 'Q') {
			q[i].f = 0;
			scanf("%d %d %d", &q[i].l, &q[i].r, &q[i].k);
		}
		else {
			q[i].f = 1;
			scanf("%d %d", &q[i].l, &q[i].r);
			cpy[idx++] = q[i].r;
		}
	}
	sort(cpy, cpy + idx);
	cnt = unique(cpy, cpy + idx) - cpy;
	root[0] = build(0, cnt - 1);
	for(int i = 1; i <= n; i++) root[i] = update(root[i-1], getId(a[i]), 1);
	for(int i = 1; i <= n; i++) S[i] = root[0];
	for(int i = 0; i < m; i++) {
		if(q[i].f == 0) printf("%d\n", cpy[query(q[i].l, q[i].r, q[i].k)]);
		else {
			add(q[i].l, getId(a[q[i].l]), -1);
			add(q[i].l, getId(q[i].r), 1);
			a[q[i].l] = q[i].r;
		}
	}	
}

代码解释

// BZOJ 1901
struct Node {
	int l, r, k, f;
}q[MaxQ];

int root[MaxN], lson[MaxN * 40], rson[MaxN * 40], sum[MaxN * 40];
int n, m, tot, cnt, idx;
int a[MaxN], cpy[MaxN];
int S[MaxN], use[MaxN];

int build(int l, int r) {
	int rt = tot++; sum[rt] = 0;
	if(l != r) {
		int mid = (l + r) >> 1;
		lson[rt] = build(l, mid);
		rson[rt] = build(mid + 1, r);
	}
	return rt;
}

int update(int last, int pos, int val) {
	//新节点 tot , 和二分区间 l r
	int rt = tot++, tmp = rt, l = 0, r = cnt - 1;
	sum[rt] = sum[last] + val;
	while(l < r) {
		int mid = (l + r) >> 1;
		if(pos <= mid) {
			lson[rt] = tot++; rson[rt] = rson[last];
			rt = lson[rt]; last = lson[last];
			r = mid;
		}
		else {
			rson[rt] = tot++; lson[rt] = lson[last];
			rt = rson[rt]; last = rson[last];
			l = mid + 1;
		}
		sum[rt] = sum[last] + val;
	}
	//tmp = tot
	return tmp;
}

int lowbit(int x) { return x & (-x); }
void add(int p, int pos, int val) {
	//因为 树状数组来把 对于 pos ~ n 后面的位置用树状数组更新 O(log2[n])
	for(int i = p; i <= n; i += lowbit(i)) S[i] = update(S[i], pos, val);
}
int getSum(int p) {
	//整体下来就是 1 ~ p 的前缀和 (线段树所记录的修改部分)
	int ans = 0;
	//相当于线段树的查询操作 use[i]节点 的左儿子 的前缀和
	for(int i = p; i > 0; i -= lowbit(i)) ans += sum[lson[use[i]]];
	return ans;
}

int query(int L, int R, int k) {
	int l_root = root[L-1], r_root = root[R];
	int l = 0, r = cnt-1;
	//use[] 数组 作为可变化的S[] 的查询数组
	for(int i = L - 1; i > 0; i -= lowbit(i)) use[i] = S[i];
	for(int i = R; i > 0; i -= lowbit(i)) use[i] = S[i];
	while(l < r) {
		int mid = (l + r) >> 1;
		//查询getsum部分是线段树的前缀和, 后面sum是原主席树的查询部分
		int tmp = getSum(R) - getSum(L-1) + sum[lson[r_root]] - sum[lson[l_root]];
		if(tmp >= k) {
			//当 前缀部分 >= k ,我们就可以直接更新use数组为 它的下一层lson
			r = mid;
			for(int i = L-1; i > 0; i -= lowbit(i)) use[i] = lson[use[i]];
			for(int i = R; i > 0; i -= lowbit(i)) use[i] = lson[use[i]];
			l_root = lson[l_root], r_root = lson[r_root];
		}
		else {
			//当 前缀部分 < k ,我们就可以直接更新use数组为 它的下一层rson
			l = mid + 1, k -= tmp;
			for(int i = L - 1; i > 0; i -= lowbit(i)) use[i] = rson[use[i]];
			for(int i = R; i > 0; i -= lowbit(i)) use[i] = rson[use[i]];
			l_root = rson[l_root], r_root = rson[r_root];
		}
	}
	return l;
}

int getId(int x) { return lower_bound(cpy, cpy + cnt, x) - cpy; }

int main() {
	tot = cnt = idx = 0;
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]); 
		cpy[idx++] = a[i];
	}
	for(int i = 0; i < m; i++) {
		char op[10]; scanf("%s", op);
		if(op[0] == 'Q') {
			// 代表查询操作
			q[i].f = 0;
			scanf("%d %d %d", &q[i].l, &q[i].r, &q[i].k);
		}
		else {
			//代表修改操作
			q[i].f = 1;
			scanf("%d %d", &q[i].l, &q[i].r);
			cpy[idx++] = q[i].r;
		}
	}
	// cpy数组要包含 修改后的值, 因为离散化要把所有的数字都进行hash
	sort(cpy, cpy + idx);
	cnt = unique(cpy, cpy + idx) - cpy;

	//建立一个权值全部为0 的线段树
	//用一个权值线段树,模拟主席树的修改部分
	root[0] = build(0, cnt - 1);
	
	//把原数组a[] 像静态主席树一样逐个插入到 主席树中
	for(int i = 1; i <= n; i++) root[i] = update(root[i-1], getId(a[i]), 1);

	//未更新的 线段树root 初始化
	for(int i = 1; i <= n; i++) S[i] = root[0];

	for(int i = 0; i < m; i++) {
		//查询操作
		if(q[i].f == 0) printf("%d\n", cpy[query(q[i].l, q[i].r, q[i].k)]);
		else {
			//更新操作
			//删掉 原值
			add(q[i].l, getId(a[q[i].l]), -1);
			//更新 新值
			add(q[i].l, getId(q[i].r), 1);
			a[q[i].l] = q[i].r;
		}
	}	
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值