数字串 (树状数组)

原题:Wannafly挑战赛15 D

题意:

给一个数字串,每次操作改变一个位置的数字,求每次操作后,有多少个子串满足以下要求:

  1. 长度在区间 [l,r] 内
  2. 首数字 > 尾数字

解析

对于一个位置p,求的是一个sum(p-r+1到p-l+1范围内数字大于p位置的个数和p+l-1到p+r-1范围内数字小于p位置的个数)
而又涉及到单点更新,那么就是树状数组了

因为数字为0~9,所以需要10棵树

tr[8][20]==3表示为:20~17(20-lowbit(20)+1)范围内8的个数为3

细节看代码吧


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<string>
#include<vector>
#include<set>
#define D long long
using namespace std;
const int N = 1e5;

int n,q,l,r;
D tr[11][N];
char x[N];

int lowbit(int i){return i&(-i);}

void up(int f,int p,int v){
    while(p<=n){
        tr[f][p]+=v;p+=lowbit(p);
    }
}

D query(int f,int l,int r){//l~r范围内f的个数
    D ans=0;
    l--;
    while(r>=1){
        ans+=tr[f][r];r-=lowbit(r);
    }
    while(l>=1){
        ans-=tr[f][l];l-=lowbit(l);
    }
    return ans;
}

void out_tree(){
    for(int i=0;i<=9;i++){
        printf("%d: ",i);
        for(int j=1;j<=n;j++){
            printf("%lld ",query(i,1,j));
        }
        printf("\n");
    }
}

int main(){
    scanf("%s",x+1);scanf("%d%d%d",&q,&l,&r);
    n=strlen(x+1);
    for(int i=1;i<=n;i++){
        up(x[i]-'0',i,1);
    }
    D ans=0;
    //初始化的时候从前往后扫,所以只需要考虑前面比它大的
    for(int i=l;i<=n;i++){
        for(int j=x[i]-'0'+1;j<=9;j++){
            ans+=query(j,max(i-r+1,1),i-l+1);
        }
    }
    //更新的时候,还要考虑后面比它小的
    while(q--){
        int pos,v;scanf("%d%d",&pos,&v);//v0 -> v
        int v0=x[pos]-'0';

        for(int i=v0+1;i<=9;i++){
            if(pos<l)break;
            ans-=query(i,max(pos-r+1,1),pos-l+1);
        }

        for(int i=0;i<v0;i++){
            if(pos+l-1>n)break;
            ans-=query(i,pos+l-1,min(pos+r-1,n));
        }

        //在减到前态的影响后及时做改变,防止影响接下来的计算
        up(v0,pos,-1);
        up(v,pos,1);
        x[pos]='0'+v;

        for(int i=v+1;i<=9;i++){
            if(pos<l)break;
            ans+=query(i,max(pos-r+1,1),pos-l+1);
        }

        for(int i=0;i<v;i++){
            if(pos+l-1>n)break;
            ans+=query(i,pos+l-1,min(pos+r-1,n));
        }

        printf("%lld\n",ans);
    }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值