题目描述
一个只含数字的字符串,q次操作,每次操作将第i位数字改为x,每次操作后,统计长度在[l, r]之间且首数字大于尾数字的子串的个数。
输入描述:
第一行一个只含数字的字符串; 第二行3个整数q, l, r; 接下来q行,每行两个整数i, x。
输出描述:
输出q行,每行一个整数,表示长度在[l, r]之间且首数字大于尾数字的子串的个数。
示例1
输入
585605 2 2 4 1 6 4 2
输出
7 8
备注:
设字符串长度为n则: 1 <= n <= 100000; 1 <= q <= 100000; 1 <= l <= r <= n; 1 <= i <= n; 0 <= x
思路:开一个二维的树状数组,0-9每个数字一维,统计区间内这个数字出现的次数.对于输入的每个位置的数查询他那个范围内比他小的数的个数.然后对于每个操作,我们只需把当前这个位置的有效个数减掉,把更新后的有效个数加上即可.
代码:
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 1000000007
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
const double esp = 1e-12;
const int ff = 0x3f3f3f3f;
map<int,int>::iterator it;
int n,q,l,r;
char s[maxn];
ll sum[11][maxn];
int lowbit(int x)
{
return x&(-x);
}
ll get_sum(int num,int x)//计算区间和
{
ll ans = 0;
for(int i = x;i>= 1;i-= lowbit(i))
ans+= sum[num][i];
return ans;
}
ll query(int num,int l,int r)
{
if(l> n||r< 1) return 0;
l = max(1,l);//注意细节
r = min(n,r);
return get_sum(num,r)-get_sum(num,l-1);
}
void add(int num,int x,int val)//更新num这个数的那一维
{
for(int i = x;i<= n;i+= lowbit(i))
sum[num][i]+= val;
}
int main()
{
scanf("%s",s+1);
cin>>q>>l>>r;
n = strlen(s+1);
for(int i = 1;i<= n;i++)
add(s[i]-'0',i,1);//更新树状数组
ll ans = 0;
for(int i = 1;i<= n;i++)
for(int j = 0;j< s[i]-'0';j++)
ans+= query(j,i+l-1,i+r-1);//查询 i+l-1,i+r-1之间比他小的数
while(q--)
{
int pos,x,now;
scanf("%d %d",&pos,&x);
now = s[pos]-'0';
add(now,pos,-1);//注意要先减掉,因为不这样的话查询新值的时候可能会把当前没有更新的值算进去
for(int j = 0;j< now;j++)
ans-= query(j,pos+l-1,pos+r-1);
for(int j = 0;j< x;j++)
ans+= query(j,pos+l-1,pos+r-1);
for(int j = now+1;j<= 9;j++)
ans-= query(j,pos-r+1,pos-l+1);
for(int j = x+1;j<= 9;j++)
ans+= query(j,pos-r+1,pos-l+1);
add(x,pos,1);
s[pos] = x+'0';
printf("%lld\n",ans);
}
return 0;
}