题意:
给一个数字串,每次操作改变一个位置的数字,求每次操作后,有多少个子串满足以下要求:
- 长度在区间 [l,r] 内
- 首数字 > 尾数字
解析:
对于一个位置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);
}
}