Queries Gym - 100741A (用结构体存10个树状数组)

题目链接: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值