花了差不多一天的时间,先大致了解了主席树这个黑科技
给上一个链接,我觉得是我看的最容易理解的主席树了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;
}
还有什么动态主席树,等变化还需一步步学习(来自菜鸡的叹息)