直接放题目然后再进行解释:
树状数组:
1.单点修改,区间查询
2.区间修改,单点查询
3.区间修改,区间查询
区间修改,单点查询
Description
给定数列a[1],a[2],…,a[n] ,你需要依次进行 q 个操作,操作有两类:
1 l r x:给定 l,r,x,对于所有i∈[l,r],将 a[i] 加上 x(换言之,将 a[l],a[l+1],…,a[r] 分别加上 x);
2 i:给定 i ,求 a[i] 的值。
Input
第一行包含 2 个正整数 n,q,表示数列长度和询问个数。保证1≤n,q≤106 。
第二行 n 个整数 a[1],a[2],…,a[n],表示初始数列。保证|a[i]|≤106 。
接下来 q 行,每行一个操作,为以下两种之一:
1 l r x:对于所有 i∈[l,r],将a[i]加上x;
2 i:给定 i,求 a[i] 的值。
保证 1≤l≤r≤106, |x|≤106。
Output
对于每个 2 i 操作,输出一行,每行有一个整数,表示所求的结果。
Samples
Input
3 2
1 2 3
1 1 3 0
2 2
Output
2
Hint
对于所有数据,1≤n,q≤106 , |a[i]|≤106, 1≤l≤r≤n, |x|≤106
AC code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read() {char ch = getchar(); int x = 0, f = 1;while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();} while('0' <= ch && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();} return x * f;}
int n,q,a,b,c,k,x,now,last;
ll t[1000005];
ll lowbit(ll x) {return x&(-x);}
void add(ll x,ll w)
{
while(x<=n)
{
t[x]+=w;
x += lowbit(x);
}
return ;
}
ll sum(ll x)
{
ll s=0;
while(x>0)
{
s+=t[x];
x -= lowbit(x);
}
return s;
}
int main()
{
n=read();
q=read();
for(ll i=1;i<=n;i++)
{
now = read();
add(i,now-last);
last = now;
}
while(q--)
{
k =read();
if(k==1)
{
a=read();
b=read();
c=read();
add(a,c);
add(b+1,-c);
}
if(k==2)
{
x = read();
printf("%lld\n",sum(x));
}
}
return 0;
}
前面的三个函数lowbit,add,sum和单点修改,区间查询是一样的,不再多做解释,可以在上一个练习查看。
要做到区间修改,我们能想到的省时间的做法是什么—差分前缀和
打个比方,也就是说我们要给区间[1,3]的每个数加上4,我们只需要给差分数组的a[1]加上4,给差分数组的a[4]减去4
a[1]+=4;
a[4]-=4;
这是因为差分在做前缀和的时候就会给每个数都加上这个数(4,0,0,-4 --> 4,4,4,0)
for(ll i=1;i<=n;i++)
{
now = read();
add(i,now-last);
last = now;
}
最开始输入的时候就要做好差分,确保这个数组是一个差分数组。
a=read();
b=read();
c=read();
add(a,c);
add(b+1,-c);
修改区间的时候也要做好差分,所以是两个add。
x = read();
printf("%lld\n",sum(x));
最后要查询哪一个位置就直接输出哪个位置的sum(x)。
因为我们在之前说过的(单点修改,区间查询)sum函数最终得到的是前缀和,那么差分数组的前缀和,某个位置的值,就是该位置实际的值。
以上只是鄙人的拙见,如果有错误、不足之处,还请指正。