A Simple Problem with Integers

原题传送门

题目

Description
You have N integers, A1, A2, … , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
“C a b c” means adding c to each of Aa, Aa+1, … , Ab. -10000 ≤ c ≤ 10000.
“Q a b” means querying the sum of Aa, Aa+1, … , Ab.

Output
You need to answer all Q commands in order. One answer in a line.

Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output
4
55
9
15

分析

这道题解法很多,可以用树状数组,线段树等数据结构轻松解决
我这里写的是一个用分块法的写法

分块法——自我感觉就是一种暴力的方法,空间复杂度超级高,拿空间换时间的典型代表,这种做法需要开好几个数组,维护很多不同的变量

a数组:存输入数据
sum数组:存分成的每一块的和
add数组:存在分块后的整块中加上了多少
pos数组:存每个元素属于哪一块
l数组:存每一块的左边界
r数组:存某一块的有边界

我的代码写的自认为还是比较清淅的,接下来我解释每一个函数:

divede函数:
这是分块操作,将原数组分成长度为n1/2相互不重叠的小块,同时求出每个块的左右边界
注意最后一块的长度不一定是n1/2,我们在这里进行了一个特殊处理

init函数:
这是预处理操作
对于每一个元素处理出它是处于那一块
对于每一块处理出这一块的区间和是多少

change函数:
这是对区间加上某个数的操作,首先求出要求的区间和是不是处于同一块中,这样就可以分成两种情况
1、处于同一块中时:直接暴力,对区间中的每一个元素加上这个数,然后更改区间和(sum数组中存了)
2、处于不同块中:对首尾执行上一条操作,中间整块的部分直接对区间加上这个数(更新add数组)

ask函数:
这是求对区间和的操作,首先求出要求的区间和是不是处于同一块中,同样分成两种情况
1、处于同一块中时:直接暴力,先累加原数组中对应区间的和,在加上对整块区间加上的数(add数组中存了)
2、处于不同块中:对首位进行上一条操作,对中间整块的部分直接用sum数组求加和,并且用add数组求对整块的加和

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, q,t;
ll a[N],sum[N], add[N];
int l[N], r[N],pos[N];
void divide()
{
    t = sqrt((double)n);
    for (int i = 1; i <= t; i++)
    {
        l[i] = (i - 1) * t + 1;
        r[i] = i * t;
    }
    if (r[t] < n)
    {
        t++;
        l[t] = r[t - 1] + 1;
        r[t] = n;
    }
}
void init()
{
    for (int i = 1; i <= t; i++)
    {
        for (int j = l[i]; j <= r[i]; j++)
        {
            pos[j] = i;
            sum[i] += a[j];
        }
    }
}
void change(int ld, int rd, ll d)
{
    int p = pos[ld], q = pos[rd];
    if (p == q)
    {
        for (int i = ld; i <= rd; i++) a[i] += d;
        sum[p] += (rd - ld + 1) * d;
    }
    else
    {
        for (int i = p + 1; i <= q - 1; i++) add[i] += d;
        for (int i = ld; i <= r[p]; i++) a[i] += d;
        sum[p] += d * (r[p] - ld + 1);
        for (int i = l[q]; i <= rd; i++) a[i] += d;
        sum[q] += d * (rd - l[q] + 1);
    }
}
ll ask(int ld, int rd)
{
    int p = pos[ld], q = pos[rd];
    ll ans = 0;
    if (p == q)
    {
        for (int i = ld; i <= rd; i++) ans += a[i];
        ans += add[p] * (rd - ld + 1);
    }
    else
    {
        for (int i = p + 1; i <= q - 1; i++) ans += sum[i] + add[i] * (r[i] - l[i] + 1);
        for (int i = ld; i <= r[p]; i++) ans += a[i];
        ans += add[p] * (r[p] - ld + 1);
        for (int i = l[q]; i <= rd; i++) ans += a[i];
        ans += add[q] * (rd - l[q] + 1);
    }
    return ans;
}
int main()
{
    cin >> n >> q;
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    divide();
    init();
    while (q--)
    {
        char op[2];
        int l, r;
        ll d;
        scanf("%s%d%d", op, &l, &r);
        if (*op == 'C')
        {
            scanf("%lld", &d);
            change(l, r, d);
        }
        else printf("%lld\n", ask(l, r));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值