题目描述
给定长度为N的数列A,然后输入M行操作指令。
第一类指令形如“C l r d”,表示把数列中第l~r个数都加d。
第二类指令形如“Q X”,表示询问数列中第x个数的值。
对于每个询问,输出一个整数表示答案。
输入格式
第一行包含两个整数N和M。
第二行包含N个整数A[i]。
接下来M行表示M条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
$1 \le N,M \le 10^5$,
$|d| \le 10000$,
$|A[i]| \le 1000000000$
样例
输入样例:
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
树状数组(区间修改,单点查询)
树状数组只支持单点查询,但是这里我们要资瓷区间操作,那么就得来一点特殊操作.
首先我们可以开一个数组B,然后对于每条C操作,我们直接利用前缀和的思想.
- 把b[l]+=d;
- 把b[r+1]-=d;
然后我们就成功达成成就,把维护序列的具体值,转化为维护指令的累计影响,每次操作的影响,在l处开始,然后在r+1处消除.然后就让单点修改可以维护区间修改.
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
#define lowbit(x) x&(-x)
int a[N],c[N],b[N],i,j,n,m;
inline int ask(int x)
{
int ans=a[x];
for(;x;x-=lowbit(x))
ans+=c[x];
return ans;
}
inline void add(int x,int y)
{
for(;x<=n;x+=lowbit(x))
c[x]+=y;
}
int main()
{
scanf("%d%d\n",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
getchar();
for(i=1;i<=m;i++)
{
char ch=getchar();
if (ch=='C')
{
int l,r,d;
scanf("%d%d%d\n",&l,&r,&d);
add(l,d);
add(r+1,-d);//前缀和思想
}
if (ch=='Q')
{
int x;
scanf(" %d\n",&x);
printf("%d\n",ask(x));
}
}
return 0;
}
刚刚学算法的初中生,问这个b数组起到了什么作用咩?
差分数组
为什么不能一开始没输入一个a接着做差分,再后续对这个差分数组进行操作呢
其实是可以的
我就这么写的连样例都过不了……
为啥啊?我觉得这个算法木有问题啊。到时我觉得我自己这个算法,不咋好。
是我老了,脑袋傻了吗?我改天修改一下,用您说的算法,做一遍啊!
用树状数组查询复杂度为 logn ,则复杂度为 m * logn,否则为 m * n 超时
把注释的方法1打开就是这样的办法了
咦,阑珊这篇题解竟然不是第一,赶紧顶上去(^-^)
感谢大佬支持😁
我再顶一个 (^_^)
蒟蒻试问:这不应该是差分思想么?
树状数组就是差分思想的数据结构。
树状数组类似于前缀和数组,而且是树型的。
前缀和如何求区间和,就是通过$s[r]-s[l-1].$
这里面就运用到了差分的思想。