题意:询问全部子树的前缀和
健值线段树裸题,区间线段树
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define L rt<<1
#define R rt<<1|1
#define mid ((ls[rt]+rs[rt])>>1)
using namespace std;//键值线段树对前后缀的统计,树状数组貌似也可做,不想思考。
int n,m;
char cmd[20];
const int N=100010;
const int inf=0x3f3f3f3f;
int val[N],ls[N<<2],rs[N<<2],ret[N<<2],mex[N<<2],root=1;
int query(int rt,int vt){//查询时注意特判
if(vt>=mex[rt])return 0;
if(ls[rt]==rs[rt])return 1;
if(mex[L]>vt)return query(L,vt)+ret[R];
else return query(R,vt);
}
void merge(int rt){//合并时加上一个log,因为应该知道右方(比自己大的有多少个)。
mex[rt]=max(mex[L],mex[R]);
ret[R]=query(R,mex[L]);
ret[rt]=ret[L]+ret[R];
}
void build(int rt,int l,int r){
ls[rt]=l,rs[rt]=r;//加上ls与rs原因是方便query时的查找与merge时的合并。
if(ls[rt]==rs[rt]){
mex[rt]=val[ls[rt]],ret[rt]=1;
return;
}
build(L,l,mid),build(R,mid+1,r);
merge(rt);
}
void update(int rt,int x){
if(ls[rt]==rs[rt]){
mex[rt]=val[ls[rt]];
ret[rt]=1;
return;
}
if(x<=mid)update(L,x);
else update(R,x);
merge(rt);
}
inline void read(int &res){
static char ch;int flag=1;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag;
}
int main(){
freopen("idol.in","r",stdin);
freopen("idol.out","w",stdout);
read(n),read(m);
for(register int i=1;i<=n;i++)read(val[i]);
build(root,1,n);//键值线段树统计区间信息
for(register int x,y,i=1;i<=m;i++){
scanf("%s",cmd);
if(cmd[0]=='Q')cout<<ret[root]<<endl;
else{
read(x),read(y),val[x]=y;
update(root,x);
}
}
return 0;
}