题目链接:https://vjudge.net/contest/181019#problem/A
题目描述:给定n和m,对于n个数字,进行下列三种操作:(1) + p r : 将p位置的元素加上r, 输出此时p位置的值;(2) - p r : 将p位置的元素减去r,若p位置的值小于r则不进行减法,输出此时p位置的值;(3) s l r mod:求区间[ l, r ]中值%m == mod的所有元素的和,输出该和。
思路:只用到两种操作,更新值和求区间和,很明显适合用树状数组解决,区间和可以转化为前缀和相减,如用sum[x]表示1~x的和,则区间[x, y]的和等于sum[y] - sum[x - 1]。(区间和转化为前缀和是很常用的技巧)因为1 <= m <= 10,0 <= mod <= m,mod最多有十个数,可以考虑建十个树状数组,将value值%m值相等的数字存到同一个树状数组中。当更新x位置的值时,直接找到x%m对应的树状数组,对x位置进行更新即可;当求和时,直接去mod对应的树状数组中求和即可。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<sstream>
#include<deque>
#include<stack>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-6;
const int maxn = 10000 + 20;
const int maxt = 300 + 10;
const int mod = 10;
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, -1, 1};
const int Dis[] = {-1, 1, -5, 5};
const int inf = 0x3f3f3f3f;
const int MOD = 1000;
int n, m, k;
ll num[maxn];
struct node{
ll tree[maxn];//树状数组
node(){
memset(tree, 0, sizeof tree);
}
inline int lowbit(int x){//如1011100返回100(这俩数是x和(x&-x)的二进制形式)
return x & -x;
}
ll get_sum(int x){//求1~x前缀和
ll sum = 0;
while(x >= 1){
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
void update(int x, ll num){//更新x位置的值
while(x <= n){
tree[x] += num;
x += lowbit(x);
}
}
ll solve(int l, int r){//前缀和转化为区间和
return get_sum(r) - get_sum(l - 1);
}
}Tree[25];//储存树状数组的结构体数组
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i){
scanf("%lld", &num[i]);
Tree[num[i] % m].update(i,num[i]);//将num[i]存入num[i]%m对应的结构体数组中,其位置还是i
}
scanf("%d", &k);
char s[5];
int p, l, r, mod;
while(k--){
scanf("%s", s);
if(s[0] == 's'){//求区间和
scanf("%d%d%d", &l, &r, &mod);
ll sum = Tree[mod].solve(l, r);
printf("%lld\n", sum);
}
else if(s[0] == '+'){
scanf("%d%d", &p, &r);
Tree[num[p] % m].update(p, -num[p]);//减去原来的num[p]
num[p] += r;//num[p]值增加r
Tree[num[p] % m].update(p, num[p]);//更新新的num[p]的值
printf("%lld\n", num[p]);
}
else if(s[0] == '-'){
scanf("%d%d", &p, &r);
if(num[p] < r){//若相减会变成复数,则不进行减法操作
printf("%lld\n", num[p]); continue;
}
Tree[num[p] % m].update(p, -num[p]);
num[p] -= r;
Tree[num[p] % m].update(p, num[p]);
printf("%lld\n", num[p]);
}
}
return 0;
}