可持久化:
可以访问历史版本的“升级版”的数据结构,利用访问历史版本的性质,可以做到许多在区间上的操作。
可持久化线段树:
例题:
cqoi2111:区间第k大
给定一个长度为n的序列,m个询问,每个询问的形式为:l,r,k表示在[l,r]间中的第k大元素.
(n<=100000, m<=100000,1<=l<=r<=n, 1<=k<=r-l+1)
如果用线段树来储存某个区间的第i大,显然是很不好实现的。因此,我们换一个思路,我们将线段树建在值域上(需要离散化),储存某个区间存在多少个数。
每插入一个新的值,那么这个值会影响线段树上的哪些节点?
这样一来,我们就做到了保存历史版本。
具体实现,我们可以再插入新节点时,插入位置以外的半个区间的指针指向上一个版本,继续走当前这个区间,直到抵达叶节点为止。
对于这道题,求解的过程很类似于在平衡树上找第k大
如果左区间的数的个数>k,走左树
反之,走右树,并将 k-左区间的数的个数
附上我的(丑)模板
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
struct node{
int cnt;
node *ch[2];
}Seg[MAXN*20],*root[MAXN],*ncnt=Seg;
int a[MAXN],b[MAXN],bn,n,m;
void init(node *p){
p->cnt=0;
p->ch[0]=p->ch[1]=NULL;
}
void build(node *&p,int l,int r){
p=++ncnt;
init(p);
if(l+1>=r)
return ;
int mid=(l+r)>>1;
build(p->ch[0],l,mid);
build(p->ch[1],mid,r);
}
void insert(node *&x,node *&y,int val,int l,int r){
x=++ncnt;
*x=*y;
x->cnt++;
if(l+1>=r)
return ;
int mid=(l+r)>>1;
if(val<mid)
insert(x->ch[0],y->ch[0],val,l,mid);
else
insert(x->ch[1],y->ch[1],val,mid,r);
}
int Pos(int x){
return lower_bound(b+1,b+1+n,x)-b;
}
void prepare(){
SF("%d%d",&n,&m);
for(int i=1;i<=n;i++){
SF("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
bn=unique(b+1,b+1+n)-b;
build(root[0],1,bn);
for(int i=1;i<=n;i++)
insert(root[i],root[i-1],Pos(a[i]),1,bn);
}
int find(node *x,node *y,int k,int l,int r){
if(l+1>=r)
return l;
int mid=(l+r)>>1;
int ts=(x->ch[0]->cnt)-(y->ch[0]->cnt);
if(ts>=k)
find(x->ch[0],y->ch[0],k,l,mid);
else
find(x->ch[1],y->ch[1],k-ts,mid,r);
}
void solve(){
int l,r,k;
for(int i=1;i<=m;i++){
SF("%d%d%d",&l,&r,&k);
PF("%d\n",b[find(root[r],root[l-1],k,1,bn)]);
}
}
int main(){
prepare();
solve();
}
//CQOI2011
另外一道简单例题:
SPOJ-COT
简单地说,可持久化地过程,就是对于每个点,均插入logn个节点来储存它所在的历史版本的信息。
可持久化Trie
例题:
HDU4757:Tree
实质上和可持久化线段树是一样的,
为了保存历史版本,我们对于每个点,都插入logn个节点
具体操作与可持久化线段树也差不多。
求解的时候,用贪心的方式即可。(即)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
struct node{
int cnt;
int ch[2];
}Seg[MAXN*20];
int ncnt,root[MAXN];
int a[MAXN],n,m,u,v;
const int len=18;
int fa[MAXN][20],deep[MAXN];
vector<int> r[MAXN];
void insert(int &x,int &y,int val,int len1){
x=++ncnt;
Seg[x]=Seg[y];
Seg[x].cnt++;
if(len1<0)
return ;
int next=(val>>len1)&1;
insert(Seg[x].ch[next],Seg[y].ch[next],val,len1-1);
}
void dfs(int x,int f,int dep){
insert(root[x],root[f],a[x],len);
fa[x][0]=f;
deep[x]=dep;
for(int i=1;i<20;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=0;i<r[x].size();i++){
if(r[x][i]==f)
continue;
dfs(r[x][i],x,dep+1);
}
}
void prepare(){
for(int i=1;i<=n;i++)
SF("%d",&a[i]);
for(int i=1;i<n;i++){
SF("%d%d",&u,&v);
r[u].push_back(v);
r[v].push_back(u);
}
dfs(1,0,1);
}
int find(int x,int y,int xx,int yy,int len1,int val){
if(len1<0)
return 0;
int res=0,x1,y1,xx1,yy1;
int k1=(val>>len1)&1;
k1^=1;
x1=Seg[x].ch[k1];
y1=Seg[y].ch[k1];
xx1=Seg[xx].ch[k1];
yy1=Seg[yy].ch[k1];
if(Seg[x1].cnt+Seg[y1].cnt>Seg[xx1].cnt+Seg[yy1].cnt){
res=find(x1,y1,xx1,yy1,len1-1,val);
if(len1!=len)
res+=(1<<len1);
}
else{
x1=Seg[x].ch[k1^1];
y1=Seg[y].ch[k1^1];
xx1=Seg[xx].ch[k1^1];
yy1=Seg[yy].ch[k1^1];
res=find(x1,y1,xx1,yy1,len1-1,val);
}
return res;
}
int lca(int x,int y){
if(deep[x]<deep[y])
swap(x,y);
for(int i=19;i>=0;i--)
if(deep[fa[x][i]]>=deep[y])
x=fa[x][i];
if(x==y)
return x;
for(int i=19;i>=0;i--)
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
void solve(){
int val;
for(int i=1;i<=m;i++){
SF("%d%d%d",&u,&v,&val);
int lcax=lca(u,v);
PF("%d\n",find(root[u],root[v],root[lcax],root[fa[lcax][0]],len,val));
}
}
void init(){
ncnt=0;
memset(Seg,0,sizeof Seg);
memset(root,0,sizeof root);
memset(fa,0,sizeof fa);
memset(deep,0,sizeof deep);
memset(a,0,sizeof a);
for(int i=1;i<=n;i++)
r[i].clear();
}
int main(){
while(SF("%d%d",&n,&m)!=EOF){
init();
prepare();
solve();
}
}