作用
普通主席树可以查询区间k小值,但若直接修改,则复杂度极大,而可修改主席树通过树状数组的辅助来修改,大大缩小了时间复杂度,缺点是空间复杂度过大.
修改的时间复杂度为O((logn)^2),空间复杂度为O(n*logn^2).
实现方法
为了减小修改复杂度,可以修改每个区间管理的范围.
传统主席树每个点对应的主席树管理一个前缀,而可修改主席树因为树状数组在处理动态区间和上有优势,每个点管理的范围就是它在树状数组中的管理范围.
具体而言,在加入元素时,对所有在树状数组中包含它的主席树都进行修改,而删除也是同一个道理.
而再查询时与树状数组类似,树状数组的查询是log的点的相加,这里也是一样,log个点一起在主席树上同时同向跳动.
log个点跳log次,故每次的复杂度为log^2.
在建树时,为了能尽可能多的共用节点,可以先建一棵根为0,什么都不管理的主席树,并将所有其他主席树的根节点先初始化为这棵主席树的根节点,最后将元素一一加入即可.
而因为有修改操作,所以在离散化前,要先将问题储存起来,将修改后的变量一起离散化.
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#define C ch=getchar()
#define lb(x) (x&(-x))
#define mid ((l+r)>>1)
#define N 10010
using namespace std;
int m,n,size,tt,num[N],root[N],b[N*2],bb,c[N*2],jia[N],jian[N],ja,jn;
struct Tree
{
Tree()
{
gs=right_son=left_son=0;
}
int left,right,left_son,right_son,gs;
} tree[N*400];
struct Cz
{
char ch;
int a,b,c;
} cz[N];
map<int,int>id;
void build(int now,int l,int r)
{
tree[now].gs=0;
tree[now].left=l;
tree[now].right=r;
if(l!=r)
{
tree[now].left_son=++tt;
build(tt,l,mid);
tree[now].right_son=++tt;
build(tt,mid+1,r);
}
}
void ad(int now,int l,int r,int u)
{
if(!now) return;
tree[now].gs++;
tree[now].left=l;
tree[now].right=r;
if(l!=r)
{
if(u<=mid)
{
tree[++tt]=tree[tree[now].left_son];
tree[now].left_son=tt;
ad(tt,l,mid,u);
return;
}
tree[++tt]=tree[tree[now].right_son];
tree[now].right_son=tt;
ad(tt,mid+1,r,u);
}
}
inline void add(int u,int v)
{
int i,j;
for(i=u; i<=n; i+=lb(i))
{
tree[++tt]=tree[root[i]];
root[i]=tt;
ad(tt,1,size,v);
}
}
void de(int now,int u)
{
tree[now].gs--;
if(tree[now].left==tree[now].right) return;
if(u<=tree[tree[now].left_son].right)
{
de(tree[now].left_son,u);
return;
}
de(tree[now].right_son,u);
}
inline void del(int u,int v)
{
int i,j;
for(i=u; i<=n; i+=lb(i))
{
de(root[i],v);
}
}
int as(int u)
{
if(tree[jia[1]].left==tree[jia[1]].right) return c[tree[jia[1]].left];
int i,j,sum=0;
for(i=1; i<=ja; i++)
{
sum+=tree[tree[jia[i]].left_son].gs;
}
for(i=1; i<=jn; i++)
{
sum-=tree[tree[jian[i]].left_son].gs;
}
if(u<=sum)
{
for(i=1; i<=ja; i++)
{
jia[i]=tree[jia[i]].left_son;
}
for(i=1; i<=jn; i++)
{
jian[i]=tree[jian[i]].left_son;
}
return as(u);
}
for(i=1; i<=ja; i++)
{
jia[i]=tree[jia[i]].right_son;
}
for(i=1; i<=jn; i++)
{
jian[i]=tree[jian[i]].right_son;
}
return as(u-sum);
}
inline int ask(int u,int v,int w)
{
int i,j;
ja=jn=0;
for(i=u; i; i-=lb(i))
{
jia[++ja]=root[i];
}
for(i=v; i; i-=lb(i))
{
jian[++jn]=root[i];
}
return as(w);
}
inline void check(int now)
{
if(!now) return;
cout<<tree[now].left<<" "<<tree[now].right<<" "<<tree[now].gs<<endl;
if(tree[now].left==tree[now].right) return;
check(tree[now].left_son);
check(tree[now].right_son);
}
int main()
{
int i,j;
char ch;
cin>>n>>m;
for(i=1; i<=n; i++)
{
scanf("%d",&num[i]);
b[i]=num[i];
}
bb=n;
for(i=1; i<=m; i++)
{
for(C; ch!='C'&&ch!='Q'; C);
cz[i].ch=ch;
scanf("%d%d",&cz[i].a,&cz[i].b);
ch=='Q'?scanf("%d",&cz[i].c):b[++bb]=cz[i].b;
}
sort(b+1,b+bb+1);
for(i=1; i<=bb; i++)
{
if(i==1||b[i]!=b[i-1])
{
id[b[i]]=++size;
c[size]=b[i];
}
}
root[0]=++tt;
build(tt,1,size);
for(i=1; i<=n; i++)
{
tree[++tt]=tree[root[0]];
root[i]=tt;
}
for(i=1; i<=n; i++)
{
add(i,id[num[i]]);
}
// check(root[1]);
for(i=1; i<=m; i++)
{
if(cz[i].ch=='Q')
{
printf("%d\n",ask(cz[i].b,cz[i].a-1,cz[i].c));
}
if(cz[i].ch=='C')
{
add(cz[i].a,id[cz[i].b]);
del(cz[i].a,id[num[cz[i].a]]);
num[cz[i].a]=cz[i].b;
}
}
}