可持久化线段树
可持久化线段树
可持久化实际上是一类思想。就是在修改的时候我们先把节点复制过来然后再复制的节点上修改。其他信息不变。
由于线段树每一次修改会改变根节点到一个节点上的路径,所以每一次修改都会新建一个根节点,所以可持久化线段树并不是一个树形结构,而是一张图。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 20000000
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,a[N];
struct PRE_DS{
struct rode{
int ls,rs,val;
};
rode p[N];
int root[N<<2],tail,tot;
inline void pushup(int k){
p[k].val=p[p[k].ls].val+p[p[k].rs].val;
}
inline int build(int l,int r){
int now=++tail;
if(l==r){
p[now].val=a[l];
return now;
}
int mid=l+r>>1;
p[now].ls=build(l,mid);
p[now].rs=build(mid+1,r);
pushup(now);
return now;
}
inline int insert(int last,int l,int r,int x,int val){
int now=++tail;
p[now]=p[last];
if(l==r){
p[now].val=val;
return now;
}
int mid=l+r>>1;
if(x<=mid) p[now].ls=insert(p[last].ls,l,mid,x,val);
else p[now].rs=insert(p[last].rs,mid+1,r,x,val);
pushup(now);
return now;
}
inline int ask(int k,int x,int l,int r){
if(l==r) return p[k].val;
int mid=l+r>>1;
if(x<=mid) return ask(p[k].ls,x,l,mid);
else return ask(p[k].rs,x,mid+1,r);
}
};
PRE_DS ds;
int main(){
n=read();m=read();
for(int i=1;i<=n;i++) a[i]=read();
ds.root[0]=ds.build(1,n);
// printf("here\n");
for(int i=1;i<=m;i++){
int v=read(),op=read();
if(op==1){
int w=read(),val=read();
ds.root[++ds.tot]=ds.insert(ds.root[v],1,n,w,val);
}
else{
int val=read();
printf("%d\n",ds.ask(ds.root[v],val,1,n));
ds.root[++ds.tot]=ds.root[v];
}
}
return 0;
}
注意这里我们仅仅修改了查询和插入,并大致是和线段树差不多的。
洛谷第二道模板题:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 700100
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
const int mod=1e9;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,a[N],id[N],rk[N],t[N];
struct rode{
int ls,rs,val;
};
rode p[N<<4];
int root[N<<2],tail,tot;
struct PRE_DS{
inline void pushup(int k){
p[k].val=p[p[k].ls].val+p[p[k].rs].val;
}
inline int build(int l,int r){
int now=++tail;
if(l==r){
p[now].val=0;
return now;
}
int mid=l+r>>1;
p[now].ls=build(l,mid);
p[now].rs=build(mid+1,r);
pushup(now);
return now;
}
inline int insert(int last,int l,int r,int x,int val){
int now=++tail;
p[now]=p[last];
if(l==r){
p[now].val+=val;
return now;
}
int mid=l+r>>1;
if(x<=mid) p[now].ls=insert(p[last].ls,l,mid,x,val);
else p[now].rs=insert(p[last].rs,mid+1,r,x,val);
pushup(now);
return now;
}
inline int ask_kth(int r1,int r2,int l,int r,int k){
if(l==r) return l;
int lcnt=p[p[r2].ls].val-p[p[r1].ls].val,mid=l+r>>1;
if(lcnt>=k) return ask_kth(p[r1].ls,p[r2].ls,l,mid,k);
else return ask_kth(p[r1].rs,p[r2].rs,mid+1,r,k-lcnt);
}
};
PRE_DS ds;
inline int disc(){
// printf("here\n");
for(int i=1;i<=n;i++) t[i]=a[i];
sort(t+1,t+n+1);
int w=unique(t+1,t+n+1)-t-1;
for(int i=1;i<=n;i++){
id[i]=lower_bound(t+1,t+w+1,a[i])-t;
rk[id[i]]=a[i];
// printf("%d\n",rk[id[i]]);
}
return w;
}
signed main(){
// freopen("P3834_6.in","r",stdin);
// freopen("my.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read(),a[i]+=mod;
disc();
// printf("n:%d\n",n);
// printf("here\n");
root[0]=ds.build(1,n);
// printf("here\n");
for(int i=1;i<=n;i++) root[i]=ds.insert(root[i-1],1,n,id[i],1);
for(int i=1;i<=m;i++){
int l=read(),r=read(),k=read();
// printf("%d %d %d\n",l,r,k);
// printf("root[l-1]:%d,root[r]:%d,k:%d\n",root[l-1],root[r],k);
printf("%d\n",rk[ds.ask_kth(root[l-1],root[r],1,n,k)]-mod);
}
return 0;
}
这是一道查询区间第 \(k\) 小的题,我们首先对他离散化一下,然后我们在查询的时候是建立权值线段树,区间第 \(k\) 小实际上是在线段树两个根节点之间查询第 \(k\) 小,我们在树上二分就可以。至于某个数到底在这段区间内出现了多少次,我们可以通过用右边的值减去左边来得到。然后二分的去递归左子树和右子树。