宣传一下算法提高课整理 <—
题目传送门点这里
题目描述
给定长度为 $N$ 的数列 $A$,然后输入 $M$ 行操作指令。
第一类指令形如 C l r d
,表示把数列中第 $l∼r$ 个数都加 $d$。
第二类指令形如 Q x
,表示询问数列中第 $x$ 个数的值。
对于每个询问,输出一个整数表示答案。
输入格式
第一行包含两个整数 $N$ 和 $M$。
第二行包含 $N$ 个整数 $A[i]$。
接下来 $M$ 行表示 $M$ 条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
$1≤N,M≤10^5,$
$|d|≤10000,$
$|A[i]|≤10^9$
样例输入
10 5
1 2 3 4 5 6 7 8 9 10
Q 4
Q 1
Q 2
C 1 6 3
Q 2
样例输出
4
1
2
5
思路
这是一道模板题,操作为区间修改,单点查询
,
普通的树状数组处理的是单点修改,区间查询
。
所以我们将原数组进行一次差分,再将差分数组加到树状数组内。
这样,原本树状数组询问的 $1$ ~ $x$ 的前缀和,就转变为了求 $x$ 的值;原本树状数组将一个点加c的操作就转变为了将区间加c。
算法时间复杂度
树状数组每次操作都是 $O(\log n)$ 的复杂度,一共 $m$ 次查询
所以时间复杂度大约是 $O(m \log n)$
AC Code
$C++$
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010;
int n, m;
int a[N], b[N]; // a是原数组,b是差分数组
int tr[N]; // 树状数组
int lowbit(int x) // 树状数组模板
{
return x & -x;
}
void add(int x, int c)
{
for (int i = x; i <= n; i += lowbit(i))
tr[i] += c;
}
int sum(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i))
res += tr[i];
return res;
}
int main()
{
int l, r, d, x;
string op;
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
b[i] = a[i] - a[i - 1]; // 将原数组变为差分数组
add(i, b[i]); // 将差分数组加入树状数组
}
while (m -- )
{
cin >> op; // 读入操作
if (op[0] == 'C')
{
scanf("%d%d%d", &l, &r, &d);
add(l, d); add(r + 1, -d);
// 第一种情况:区间修改,这是差分的基本操作,将b[l]加c,b[r+1]减c
}
if (op[0] == 'Q')
{
scanf("%d", &x);
int res = sum(x);
printf("%d\n", res);
// 第二种情况:单点查询,相当于求差分数组的前缀和
}
}
return 0;
}
最后,如果觉得对您有帮助的话,点个赞再走吧!
orz
!