跳转至

可持久化数组

如题,你需要维护这样的一个长度为 \(N\) 的数组,支持如下几种操作

  1. 对于操作1,格式为\(v_i \ 1 \ p_i \ {value}_i\),即为在版本\(v_i\)的基础上,将 \(a_{p_i}\) 修改为 \({value}_i\)

  2. 对于操作2,格式为\(v_i \ 2 \ p_i\),即访问版本\(v_i\)中的 \(a_{p_i}\)的值,注意:生成一样版本的对象应为 \(v_i\)

此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)

代码

还是很简单的。

/*////////ACACACACACACAC///////////
       . Coding by Ntsc .
       . FancyKnowledge .
       . Prove Yourself .
/*////////ACACACACACACAC///////////

//
#include<bits/stdc++.h>

//
#define int long long
#define ull unsigned long long
#define db double
#define endl '\n'
#define err(fmt, ...) fprintf(stderr, "[%d] : " fmt "\n", __LINE__, ##__VA_ARGS__)
///*
#define pr pair<double,int>
#define pf first
#define ps second
#define pb push_back
//*/

//
using namespace std;
//
const int N=1e6+5;
const int M=1e3;
int MOD=1e9+7;
const int MMOD=903250223;
const int INF=1e9;
const int IINF=1e18;
const db eps=1e-9;
//
int n,m,a[N],b,q,sw,k,ans,res,tmp,cnt,mul[N];
int  rt[N];
int vcnt;//版本计数
int L;

struct node{
    int lc,rc,v;
}tr[N<<5];//16倍空间

void pushup(int x){
    //
}

void build(int &x,int l,int r){
    x=++cnt;
    if(l==r){
        tr[x].v=a[l];
        return ;
    }
    int mid=l+r>>1;
    build(tr[x].lc,l,mid);
    build(tr[x].rc,mid+1,r);
}

void insert(int pre,int &now,int l,int r,int p,int v){
    now=++cnt;//建立新节点
    if(l==r){
        tr[now].v=v;
        return ;
    }
    int mid=l+r>>1;
//  tr[now]=tr[pre];
//  tr[now].v=tr[pre].v;//更新,因为如下
    if(p<=mid){
        tr[now].rc=tr[pre].rc;//也可以在一开始复制旧的点的信息
        insert(tr[pre].lc,tr[now].lc,l,mid,p,v);
    }
    else {
        tr[now].lc=tr[pre].lc;
        insert(tr[pre].rc,tr[now].rc,mid+1,r,p,v);
    }

//  pushup(now);
}

int query(int x,int l,int r,int p){
    if(l==r)return tr[x].v;

    int mid=l+r>>1;
    if(p<=mid)return query(tr[x].lc,l,mid,p);
    return query(tr[x].rc,mid+1,r,p);
}

signed main(){

//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
//  freopen(".txt","w",stderr);

    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
//      cin>>a[i];
        scanf("%lld",&a[i]);
    }

    build(rt[0],1,n);

    while(m--){
        int v,op,loc,val;
//      cin>>v>>op>>loc;
        scanf("%lld%lld%lld",&v,&op,&loc);
        if(op==1){
            scanf("%lld",&val);
            insert(rt[v],rt[++vcnt],1,n,loc,val);
//          cerr<<"rt"<<vcnt<<"]="<<tr[rt[vcnt]].rc<<endl;
        }else{
            cout<<query(rt[v],1,n,loc)<<endl;
            rt[++vcnt]=rt[v];
        }
    }

    return 0;
}

//check your long long and the size of memery!!!

树上版可持久化线段树求k小(树上主席树)

www.luogu.com.cn

模型

求树上从x-y路径上的地k小值

做法

我们考虑进行树上差分。我们对每一个节点x维护一个权值线段树t_x,代表着从x到根节点的路径上的数值发布情况。

对于树的构建,我们这样:在x的父亲的版本基础上加入x,就形成了x版本的线段树。

对于查询,我们工具树上差分得出代表链x-y的树应该是\(t_x+t_y-t_{lca}-t_{fa(lca)}\),于是我们4个指针分别指向即可。

时刻记住,主席树的功能是让你快速求出特定区间内某值域范围的数字个数,方便二分,而不是直接记录答案!