动态主席树

51 篇文章 1 订阅
18 篇文章 1 订阅

经过几天的疯狂学习,咸鱼博主终于学会了动态主席树!

书接上文。
在上一篇博文里学习了静态主席树的写法。
核心思想就是在原有的基础上建立新的线段树。
但是那只能解决静态的问题。
如果我们在原有的查询操作上加入单点修改,又该如何用主席树实现呢?

例子 1 2 10 9 8
建好线段树森林(省略~)
操作 更改 2 位置的数 变为 6
如果我们直接暴力对从2开始的每一个线段树都进行一次修改
时间复杂度又爆炸了 失去了我们用主席树的初衷

对于修改操作影响的线段树,我们发现影响是相同的。
即 原来的数出现的区间 以及 现在的数出现的区间
于是用树状数组来维护修改操作

以下为引用大佬之语

对于更新, 我们不改变这些已经建好的树, 而是另建一批树S,用来记录更新,而这批线段树,我们用树状数组来维护

也就是树状数组的每个节点都是一颗线段树

一开始,S[0]、S[1]、S[2]、S[3]、S[4]、S[5] (树状数组的每个节点) 这些都与T[0]相同(也就是每个节点建了一棵空树)

对于C 2 6 这个操作, 我们只需要减去一个2,加上一个6即可
对于减去2
(树状数组i+lowbit(i)为i的父亲节点, 修改i,就要把i的所有父亲节点都修改了)
2在树状数组中出现的位置是 2、2+lowbit(2)=4 这两个位置,
因此要更新的是S[2]和S[4]这两个节点中的树

当查询的时候, 对树T的操作与静态的一致,另外再加上S树的值就好了

感谢这位大大,讲解的很明白链接

下面给出裸码

裸题

#include <cstdio>
#include <iostream>
#include <algorithm>
#define il inline
using namespace std;
const int maxm=1e6+1;
int n;
int root[maxm],lson[maxm<<5],rson[maxm<<5],sum[maxm<<5];
int a[maxm],val[maxm];
int s[maxm];
int used[maxm]; 
struct node{
    bool f;
    int l,r,num;
}opt[maxm];
int size,tot,t,m;
int build(int l,int r)
{
    int rt=++size;
    sum[rt]=0;
    if(l!=r)
    {
        int mid=(l+r)>>1;
        lson[rt]=build(l,mid);
        rson[rt]=build(mid+1,r);
    }
    return rt;
}
int updata(int pre,int l,int r,int num,int x)
{
    int rt=++size;
    lson[rt]=lson[pre],rson[rt]=rson[pre],sum[rt]=sum[pre]+x;
    if(l<r)
    {
        int mid=(l+r)>>1;
        if(num<=mid) lson[rt]=updata(lson[pre],l,mid,num,x);
        else rson[rt]=updata(rson[pre],mid+1,r,num,x); 
    }
    return rt;
}
il int lowbit(int x)
{
    return x&(-x);
} 
il int Sum(int now) 
{
    int ret=0;
    for(int i=now;i>0;i-=lowbit(i))
     ret+=sum[lson[used[i]]];
    return ret;
}
int find_rank(int ql,int qr,int pre,int now,int l,int r,int k)
{
    if(l>=r) return l;
    int mid=(l+r)>>1;
    int nowsum=Sum(qr)-Sum(ql)+sum[lson[now]]-sum[lson[pre]];
    if(nowsum>=k)
    {
        for(int i=ql;i>0;i-=lowbit(i))
         used[i]=lson[used[i]];
        for(int i=qr;i>0;i-=lowbit(i))
         used[i]=lson[used[i]];
        return find_rank(ql,qr,lson[pre],lson[now],l,mid,k);
    }
    else
    {
        for(int i=ql;i>0;i-=lowbit(i))
         used[i]=rson[used[i]];
        for(int i=qr;i>0;i-=lowbit(i))
         used[i]=rson[used[i]];
        return find_rank(ql,qr,rson[pre],rson[now],mid+1,r,k-nowsum);
    }
}
il void modify(int x,int x_val,int v)
{
    for(int i=x;i<=n;i+=lowbit(i))
     s[i]=updata(s[i],1,t,x_val,v);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&val[i]);
        a[i]=val[i];
    }
    tot=n;
    for(int i=1;i<=m;i++)
    {
        char c[10];
        scanf("%s",c);
        opt[i].f=(c[0]=='Q');
        if(c[0]=='Q')
         scanf("%d%d%d",&opt[i].l,&opt[i].r,&opt[i].num);
        else
         scanf("%d%d",&opt[i].l,&opt[i].num),val[++tot]=opt[i].num; 
    }
    sort(val+1,val+tot+1);
    t=unique(val+1,val+tot+1)-val-1;
    root[0]=build(1,t);
    for(int i=1;i<=n;i++)
    {
        int id=lower_bound(val+1,val+t+1,a[i])-val;
        root[i]=updata(root[i-1],1,t,id,1);
    }
    for(int i=1;i<=n;i++)
     s[i]=root[0];

    for(int i=1;i<=m;i++)
    {
        if(opt[i].f)
        {
            for(int j=opt[i].l-1;j>0;j-=lowbit(j))
             used[j]=s[j];
            for(int j=opt[i].r;j>0;j-=lowbit(j))
             used[j]=s[j];
            printf("%d\n",val[find_rank(opt[i].l-1,opt[i].r,root[opt[i].l-1],root[opt[i].r],1,t,opt[i].num)]);
        }
        else
        {
            modify(opt[i].l,lower_bound(val+1,val+t+1,a[opt[i].l])-val,-1);
            modify(opt[i].l,lower_bound(val+1,val+t+1,opt[i].num)-val,1);
            a[opt[i].l]=opt[i].num;
        }
    }

    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值