7GOJ 偶像 [线段树]

题意:询问全部子树的前缀和
健值线段树裸题,区间线段树

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值