经过几天的疯狂学习,咸鱼博主终于学会了动态主席树!
书接上文。
在上一篇博文里学习了静态主席树的写法。
核心思想就是在原有的基础上建立新的线段树。
但是那只能解决静态的问题。
如果我们在原有的查询操作上加入单点修改,又该如何用主席树实现呢?
例子 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;
}