区间修改区间查询 【ybt高效进阶4-2-4】
题目大意:
给定一个数组,让其支持区间修改,区间求和的操作。
思路:
树状数组+差分
我们设
d
d
d数组是
a
a
a数组的差分数组,那么我们有
a
[
i
]
=
∑
j
=
1
i
d
[
j
]
a[i]=\sum\limits_{j=1}^{i}d[j]
a[i]=j=1∑id[j]
那么求区间的前缀和类比单点修改区间查询,用相减法,区间末值-区间开头。
那么我们有:
∑
i
=
1
n
a
[
i
]
=
∑
i
=
1
n
∑
j
=
1
i
d
[
j
]
\sum\limits_{i=1}^{n}a[i]=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i}d[j]
i=1∑na[i]=i=1∑nj=1∑id[j]
展开这个式子,观察每个项,我们发现,
d
[
1
]
d[1]
d[1]出现了
n
n
n次,
d
[
2
]
d[2]
d[2]出现了
n
−
1
n-1
n−1次,以此类推,
d
[
i
]
d[i]
d[i]出现了
n
−
i
+
1
n-i+1
n−i+1次。于是我们得到了:
∑
i
=
1
n
a
[
i
]
=
∑
i
=
1
n
d
[
i
]
∗
(
n
−
i
+
1
)
=
∑
i
=
1
n
d
[
i
]
∗
(
n
+
1
)
−
d
[
i
]
∗
i
\sum\limits_{i=1}^{n}a[i]=\sum\limits_{i=1}^{n}d[i]*(n-i+1)=\sum\limits_{i=1}^{n}d[i]*(n+1)-d[i]*i
i=1∑na[i]=i=1∑nd[i]∗(n−i+1)=i=1∑nd[i]∗(n+1)−d[i]∗i
那么我们使用树状数组维护
d
[
i
]
d[i]
d[i]和
d
[
i
]
∗
i
d[i]*i
d[i]∗i这两个值就好了,差分时记得右边界要+1!!!
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<vector>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=1e6+50,p=1e9+7;
ll n,m,c[2][V],a[V];
ll x,y,k,otp;
ll lowbit(ll x) { return x&(-x); }
void add(ll x,ll y)
{
for(r ll i=x;i<=n;i+=lowbit(i))
{
c[0][i]+=y;
c[1][i]+=y*x; //两个树状数组维护上面所说的值
}
}
ll ask(ll x)
{
ll res=0;
for(r ll i=x;i;i-=lowbit(i))
res+=c[0][i]*(x+1)-c[1][i]; //代入公式
return res;
}
char buf[1<<23], *p1=buf, *p2=buf, obuf[1<<23], *O=obuf; //超级加速快读
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++)
ll in()
{
ll res=0,f=1;
char ch;
while((ch=getchar())<'0'||ch>'9')
if(ch=='-') f=-1;
res=res*10+(ch-'0');
while((ch=getchar())>='0'&&ch<='9')
res=res*10+(ch-'0');
return res*f;
}
void put(ll x)//普通的快输
{
if(x<0) putchar('-'),x=-x;
if(x>9) put(x/10);
putchar(x%10+'0');
}
int main()
{
n=in(),m=in(); //建议使用快读,要不很可能会被卡常
rep(i,1,n)
{
a[i]=in();
add(i,a[i]);
add(i+1,-a[i]);
}
rep(i,1,m)
{
otp=in();
if(otp==1)
{
x=in(),y=in(),k=in();
add(x,k);
add(y+1,-k);//右边界要+1
}
else
{
x=in(),y=in();
put(ask(y)-ask(x-1));
putchar(10);
}
}
return 0;
}