主席树初体会

花了差不多一天的时间,先大致了解了主席树这个黑科技

给上一个链接,我觉得是我看的最容易理解的主席树了https://www.cnblogs.com/zyf0163/p/4749042.html

为了更方便你们理解,我对他的代码增加了我的理解这是一道hdu的题目,一道板子题建议做一下http://acm.hdu.edu.cn/showproblem.php?pid=2665

这里附上代码详解(不对之处望指正)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000 + 5;

int a[N], b[N], rt[N * 20], ls[N * 20], rs[N * 20], sum[N * 20];
//rt[]是记录着第几个父节点
//ls[]是左分支 存着对应节点的左分支 
//rs[]是右分支 存着对应节点的右分支
//sum[]是出现的次数 
int n, k, tot, sz, ql, qr, x, q, T;

void Build(int &o, int l, int r){
    o = ++ tot;//tot是解决存储在sum中的位置 
    sum[o] = 0;//初值都是0 
    if(l == r) return;
    int m = (l + r) >> 1;
    Build(ls[o], l, m);//完善左支 
    Build(rs[o], m + 1, r);//再完善右支 
}

void update(int &o, int l, int r, int last, int p){
    o = ++ tot;//last就是前面的一棵树 
    ls[o] = ls[last];
    rs[o] = rs[last];
    sum[o] = sum[last] + 1;
    if(l == r) return;
    int m = (l + r) >> 1;
    if(p <= m)  update(ls[o], l, m, ls[last], p);//根据p出现的位置只更新掉一条路 ,ls[o]被tot更新重新分配了在sum中的位置 
    else update(rs[o], m + 1, r, rs[last], p);
}

int query(int ss, int tt, int l, int r, int k){
    if(l == r) return l;//最后返回的是他在b数组里出现的位置 
    int m = (l + r) >> 1;
    int cnt = sum[ls[tt]] - sum[ls[ss]];//在所给的范围中,出现在b数组中l~m位置的个数 
    if(k <= cnt) return query(ls[ss], ls[tt], l, m, k);
    else return query(rs[ss], rs[tt], m + 1, r, k - cnt);
}

void work(){
    scanf("%d%d%d", &ql, &qr, &x);
    int ans = query(rt[ql - 1], rt[qr], 1, sz, x);
    printf("%d\n", b[ans]);
}

int main(){
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &q);
        for(int i = 1; i <= n; i ++) scanf("%d", a + i), b[i] = a[i];
        sort(b + 1, b + n + 1);//b数组排序 
        sz = unique(b + 1, b + n + 1) - (b + 1);//不重复的有多少个 
        tot = 0;
        Build(rt[0],1, sz);//总共有sz个不重复的数,所以最下面一层有1~sz个
		 
//        for(int i = 0; i <= 4 * n; i ++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d\n", i, rt[i], ls[i], rs[i], sum[i]);
          for(int i = 1; i <= n; i ++)a[i] = lower_bound(b + 1, b + sz + 1, a[i]) - b;//a[i]变成记录a[i]元素在b数组中的位置 
          for(int i = 1; i <= n; i ++)update(rt[i], 1, sz, rt[i - 1], a[i]);//从1更新到n,主席树完成 
//        for(int i = 0; i <= 5 * n; i ++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d\n", i, rt[i], ls[i], rs[i], sum[i]);
        while(q --)work();
    }
    return 0;
}

P3919 【模板】可持久化数组(可持久化线段树/平衡树)


#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define drep(i,a,b)	for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int N = 1e6+5;
int a[N],b[N],rt[N*20],ls[N*20],rs[N*20];
ll sum[N*20];
int tot,sz; 
void Build(int &o,int l,int r) {
	o=++ tot;
	if(l == r){
		sum[o]=a[l];
		return;
	} 
	int m=(l+r)>>1;
	Build(ls[o],l,m);
	Build(rs[o],m+1,r);
}
void update(int &o,int l,int r,int last,int p,int c,bool fg) {
	o=++tot;
	ls[o]=ls[last];
	rs[o]=rs[last];
	if(fg) return ;
	if(l == r){
		sum[o]=c;
		return;
	} 
	int m=(l+r)>>1;
	if(p<=m)  update(ls[o],l,m,ls[last],p,c,fg);
	else update(rs[o],m+1,r,rs[last],p,c,fg);
}
ll query(int id, int l,int r,int k) {
	if(l == r)	return sum[id];
	int m=(l+r)>>1;
	if(k<=m) return query(ls[id],l,m,k);
	else return query(rs[id],m+1,r,k);
}
int n,m,x,y,c,d;
int main() {
	SC(n,m);
	rep(i,1,n) sc(a[i]);
	tot=0;
	Build(rt[0],1,n);
	rep(i,1,m){
		sc(x),sc(y),sc(c);
		if(y==1){
			sc(d);
			update(rt[i],1,n,rt[x],c,d,0);
		}
		else{
			cout<<query(rt[x],1,n,c)<<endl;
			update(rt[i],1,n,rt[x],0,0,1);
		}
	}
	return 0;
}

 

 

还有什么动态主席树,等变化还需一步步学习(来自菜鸡的叹息)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值