Wannafly挑战赛15 数字串(树状数组)

题目描述 

一个只含数字的字符串,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;
}

猜你喜欢

转载自blog.csdn.net/nka_kun/article/details/80290599