可持续数据结构(可持续字典树,线段树)

这篇博客介绍了两种高效的数据结构——可持续字典树和可持续线段树,并展示了它们在区间查询和更新操作中的应用。字典树用于求区间内的异或值最大值,线段树则用于查询区间内第K大的数。通过这两个数据结构,可以实现快速的区间操作,提高算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可持续字典树

可持续字典树
题意:求区间内从一点开始到结尾的异或值最大

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=600010;
const int M=N*25;
int n,m;
int s[N];
int tr[M][2];
int mxa_id[M];
int root[N];
int idx;
void inset(int i,int k,int p,int q)//i 下标   k当前处理到第几位  P上一个版本  q当前版本
{
    if(k<0)
    {
        mxa_id[q]=i;
        return ;
    }
    int v=s[i]>>k&1;
    if(p)
    {
        tr[q][v^1]=tr[p][v^1];//将上个版本复制过来
    }
    tr[q][v]=++idx;
    inset(i,k-1,tr[p][v],tr[q][v]);
    mxa_id[q]=max(mxa_id[tr[q][0]],mxa_id[tr[q][1]]);
}
int query(int root,int c,int l)//查询最大异或值
{
    int p=root;
    for(int  i=23; i>=0; i--)
    {
        int v=c>>i&1;
        if (mxa_id[tr[p][v ^ 1]] >= l)
            p = tr[p][v ^ 1];
        else
        {
            p=tr[p][v];
        }
    }
    return c^s[mxa_id[p]];
}
int main()
{
    cin>>n>>m;
    mxa_id[0]=-1;
    root[0]=++idx;
    inset(0,23,0,root[0]);
    for(int i=1; i<=n; i++)
    {
        int x;
        scanf("%d",&x);
        s[i]=s[i-1]^x;
        root[i]=++idx;
        inset(i,23,root[i-1],root[i]);
    }
    char op[2];
    int l,r,x;
    while(m--)
    {
        scanf("%s", op);
        if(*op=='A')
        {
            scanf("%d",&x);
            n++;
            s[n]=s[n-1]^x;//前缀和加速
            root[n]=++idx;
            inset(n,23,root[n-1],root[n]);

        }
        else
        {
            scanf("%d%d%d", &l, &r, &x);
            printf("%d\n", query(root[r - 1], s[n] ^ x, l - 1));
        }
    }
    return 0;
}

可持续线段树

求区间第K大的数
注:root[r]-root[l-1]等于再次期间内添加了多少个数

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=1e5+10;
int n,m;
int a[N];
vector<int> v;
struct node
{
    int l,r;
    int cnt;//存储区间内有几个数
}tree[22*N];
int root[N],tot;//记录版本
int finds(int x)//二分查找离散化后的下标
{
   return lower_bound(v.begin(), v.end(), x) - v.begin();
}
int build(int l,int r)//建树

{
    int p=++tot;
    if(l==r)
    {
        return p;
    }
    int mid=(l+r)/2;
    tree[p].l=build(l,mid);
    tree[p].l=build(mid+1,r);
    return p;

}
int inset(int u,int l,int r,int k)//插入修改区间数的个数
{
    int q=++tot;
    tree[q]=tree[u];
    if(l==r)
    {
        tree[q].cnt++;
        return q;
    }
    int mid=(l+r)/2;
    if(k>mid)
    {
        tree[q].r=inset(tree[u].r,mid+1,r,k);
    }
    else
    {
        tree[q].l=inset(tree[u].l,l,mid,k);
    }
    tree[q].cnt=tree[tree[q].l].cnt+tree[tree[q].r].cnt;
    return q;
}
int query(int q,int p,int l,int r,int k)//查询第K大的数
{
    if(l==r)
    {
        return r;
    }
    int cnt=tree[tree[q].l].cnt-tree[tree[p].l].cnt;
    int mid=(l+r)/2;
    if(k<=cnt)
    {
        return query(tree[q].l,tree[p].l,l,mid,k);
    }
    else
    {
        return query(tree[q].r,tree[p].r,mid+1,r,k-cnt);
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    root[0]=build(0,v.size()-1);
    for(int i=1;i<=n;i++)
    {
        root[i]=inset(root[i-1],0,v.size()-1,finds(a[i]));
    }
    while(m--)
    {
        int l,r,k;
        cin>>l>>r>>k;
        cout<<v[query(root[r],root[l-1],0,v.size()-1,k)]<<endl;//query查询的是离散化后的下标
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值