区间修改区间查询
题目链接:YbtOJ
解题思路
区间操作,我们可以想到差分。
设差分数组为
d
d
d ,那么求
∑
i
=
1
n
a
i
\sum\limits_{i=1}^na_i
i=1∑nai 就可以转换为
∑
i
=
1
n
∑
j
=
1
i
d
i
\sum\limits_{i=1}^n\sum\limits_{j=1}^id_i
i=1∑nj=1∑idi 。
暴力枚举是
O
(
n
2
)
O(n^2)
O(n2) 的,我们考虑优化。
我们可以发现:
d
1
d_1
d1 出现了
n
n
n 次,
d
2
d_2
d2 出现了
n
−
1
n-1
n−1 次,
d
i
d_i
di 出现了
n
−
i
+
1
n-i+1
n−i+1 次,所以
∑
i
=
1
n
a
i
=
∑
i
=
1
n
(
d
i
∗
(
n
+
1
)
−
d
i
∗
i
)
\sum\limits_{i=1}^na_i=\sum\limits_{i=1}^n(d_i*(n+1)-d_i*i)
i=1∑nai=i=1∑n(di∗(n+1)−di∗i),我们用树状数组维护
d
i
d_i
di 和
d
i
∗
i
d_i*i
di∗i 即可。
code
#include<iostream>
#include<cstdio>
#define lb(x) (x&(-x))
#define int long long
using namespace std;
int n,q;
int c[1000010];
int c1[1000010];
void in(int x,int y)
{
for(int i=x;i<=n;i+=lb(i))
c[i]+=y,c1[i]+=x*y;
}
int fd(int x)
{
int s=0;
for(int i=x;i;i-=lb(i))
s+=c[i]*(x+1)-c1[i];
return s;
}
signed main()
{
cin>>n>>q;
for(int i=1;i<=n;i++)
{
int t;
scanf("%lld",&t);
in(i,t);
in(i+1,-t);
}
while(q--)
{
int t,l,r;
scanf("%lld%lld%lld",&t,&l,&r);
if(t==1)
{
int x;
scanf("%lld",&x);
in(l,x);
in(r+1,-x);
}
else
cout<<fd(r)-fd(l-1)<<endl;
}
}