题目描述
如题,已知一个数列,你需要进行下面两种操作:
-
将某区间每一个数加上 ;
-
求出某一个数的值。
输入格式
第一行包含两个整数 、,分别表示该数列数字的个数和操作的总个数。
第二行包含 个用空格分隔的整数,其中第 个数字表示数列第 项的初始值。
接下来 行每行包含 或 个整数,表示一个操作,具体如下:
操作 : 格式:1 x y k 含义:将区间 内每个数加上 ;
操作 : 格式:2 x 含义:输出第 个数的值。
输出格式
输出包含若干行整数,即为所有操作 的结果。
样例输入 #1
5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4
样例输出 #1
6
10
数据约束
,保证任意时刻序列中任意元素的绝对值都不大于 。
题目分析
相较于上一题而言,本题所作出的改变为区间修改。
我们知道上次每次单点修改数值的复杂度为 ,若本题仍用这种解法,则单纯修改数值的总复杂度为 , 实在是卧龙凤雏。
在这里,我们引入差分数组的解法。我们知道对于差分数组而言,当范围修改原数组某一段区间的数值时,相当于修改差分数组的两个范围的边界值。
而对于本题的查询操作,对于单点数值的查询,相当于求差分数组的前缀和,与树状数组的适用不谋而合。
这样,我们只需要将原本的数组转换为差分数组,并按树状数组的方式维护,最终修改数值的复杂的为 ,查询数值的复杂度也为 。
Accept代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 500010;
int n, m;
int a[N], c[N];
void modify(int x, int k)
{
for ( ; x <= n; x += x & (-x)) c[x] += k;
}
int query(int x)
{
int s = 0;
for ( ; x; x -= x & (-x)) s += c[x];
return s;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
modify(i, a[i] - a[i - 1]);
}
for (int i = 0; i < m; i ++)
{
int op; cin >> op;
if (op == 1)
{
int x, y, k;
cin >> x >> y >> k;
modify(x, k), modify(y + 1, -k);
}
else
{
int x; cin >> x;
cout << query(x) << "\n";
}
}
return 0;
}