可持久化数据结构

可持久化trie树

最大异或和

#include <iostream>

using namespace std;

const int N = 600010, M = N * 25;

int n, m;  // 序列初始长度、操作个数
int s[N];  // 原序列的异或前缀和
int tr[M][2];  // trie中的节点
int max_id[M];  // 以当前节点为根的子树所代表的数据中最晚插入的那个数据插入的时间t(即第几个插入的)
int root[N], idx;  // 记录每个版本,从1开始

// i: 前缀和下标, 表示插入Si
// k: 当前处理到Si的第几位, 从23为开始处理到第0位
// p: 上一个版本的根节点
// q: 当前版本的根节点, 是p的复制, 差别在于新加了一条链
void insert(int i, int k, int p, int q) {
    
    if (k < 0) {  // 说明我们处理完了最后一位(第0位), 此时q就是叶节点的编号
        max_id[q] = i;
        return;
    }
    int v = s[i] >> k & 1;
    // 所有需要修改的节点,不要去修改它,而是拷贝一个新的,然后修改这个新点。
    if (p) tr[q][v ^ 1] = tr[p][v ^ 1];  // 如果上一个版本存在,直接复制过来
    tr[q][v] = ++idx;  // 当前这个二进制位需要开辟一个新的节点
    insert(i, k - 1, tr[p][v], tr[q][v]);
    max_id[q] = max(max_id[tr[q][0]], max_id[tr[q][1]]);  // 左右儿子取大
}

// 从root开始查询, 需要被异或的数据是C, 区间左端点是L
int query(int root, int C, int L) {
    
    int p = root;
    for (int i = 23; i >= 0; i--) {
        int v = C >> i & 1;
        if (max_id[tr[p][v ^ 1]] >= L) p = tr[p][v ^ 1];
        else p = tr[p][v];  // 找不到更好的了,只能将就一下
    }
    return C ^ s[max_id[p]];  // 这个叶子节点只有它自己,因此max_id就是Si的下标i
}

int main() {
    
    scanf("%d%d", &n, &m);
    
    // 这里max_id[0]中的0表示空节点,空节点不包含任何点,
    // 所以它的max_id需要设置成比所有的id都小的数,所以设置成任何负数均可。
    max_id[0] = -1;
    
    root[0] = ++idx;  // 第0个版本在trie中的根节点编号为1
    insert(0, 23, 0, root[0]);  // 在第0个版本中插入S0, 不存在上一个版本
    
    for (int i = 1; i <= n; i++) {
        int x;
        scanf("%d", &x);
        s[i] = s[i - 1] ^ x;
        root[i] = ++idx;  // 第i个版本
        insert(i, 23, root[i - 1], root[i]);
    }
    
    char op[2];
    int l, r, x;
    for (int i = 1; i <= m; i++) {
        scanf("%s", op);
        if (*op == 'A') {
            scanf("%d", &x);
            n++;
            s[n] = s[n - 1] ^ x;
            root[n] = ++idx;
            insert(n, 23, root[n - 1], root[n]);
        } else {
            scanf("%d%d%d", &l, &r, &x);
            // p在[L, R]之间,p-1在[L-1, R-1]之间
            printf("%d\n", query(root[r - 1], s[n] ^ x, l - 1));
        }
    }
    
    return 0;
}

可持久化线段树(主席树)

详解

第k小数

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int n,m,len,a[N],d[N];
int rout[N],idx;
struct tree{
    int l,r,v;
}t[N*20];
int build(int l,int r){
    int p=++idx;
    int mid=(l+r)>>1;
    if(l<r){
        t[p].l=build(l,mid);
        t[p].r=build(mid+1,r);
    }
    t[p].v=0;
    return p;
}
int inserts(int pre,int l,int r,int v){
    int p=++idx,mid=(l+r)>>1;
    t[p].l=t[pre].l;
    t[p].r=t[pre].r;
    t[p].v=t[pre].v+1;
    if(l<r){
        if(v<=mid)t[p].l=inserts(t[pre].l,l,mid,v);
        else t[p].r=inserts(t[pre].r,mid+1,r,v);
    }
    return p;
}
int searchs(int x,int y,int l,int r,int k){
    if(l==r)return l;
    int sum=t[t[y].l].v-t[t[x].l].v;
    int mid=(l+r)>>1;
    if(k<=sum)return searchs(t[x].l,t[y].l,l,mid,k);
    else return searchs(t[x].r,t[y].r,mid+1,r,k-sum);
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        d[i]=a[i];
    }
    sort(d+1,d+1+n);
    int len=unique(d+1,d+n+1)-(d+1);
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(d+1,d+len+1,a[i])-d;
    }
    rout[0]=build(1,len);
    for(int i=1;i<=n;i++){
        rout[i]=inserts(rout[i-1],1,len,a[i]);
    }
    while(m--){
        int l,r,k;
        cin>>l>>r>>k;
        int ans=searchs(rout[l-1],rout[r],1,len,k);
        cout<<d[ans]<<endl;
    }
}

 

posted @ 2022-11-05 23:41  Dengpc  阅读(62)  评论(0)    收藏  举报