题目描述
如题,已知一个数列,你需要进行下面两种操作:
将某区间每一个数加上 kk。
求出某区间每一个数的和。
输入格式
第一行包含两个整数 n, mn,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。
接下来 mm 行每行包含 33 或 44 个整数,表示一个操作,具体如下:
1 x y k:将区间 [x, y][x,y] 内每个数加上 kk。
2 x y:输出区间 [x, y][x,y] 内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 的结果。
首先我们得晓得基本的树状数组的可以完成的操作是:
1单点修改
2区间查询
但是如果预处理了成了差分树状数组的话就可以完成
1区间修改
2单点查询
而现在题目的意思是区间修改和区间查询
现在定义数组a是原数组
b是a的差分数组
现在我们想要求a[1]+a[2]+a[3]+…+a[x];
也就是求a的某个前缀和
a[1]+a[2]+a[3]+…+a[x]=b[1]+(b[1]+b[2])+(b[1]+b[2]+b[3])+…(b[1]+b[2]+…b[x])
将这个公式竖着放一下
a[1]=b[1]
a[2]=b[1]+b[2]
a[3]=b[1]+b[2]+b[3]
a[4]=b[1]+b[2]+b[3]+b[4]
.
.
.
a[x]=b[1]+b[2]+b[3]+b[4] …+b[x]
然后这样补全
第0行b[1]+b[2]+b[3]+b[4] …+b[x]
第1行b[1]+b[2]+b[3]+b[4] …+b[x]
第2行b[1]+b[2]+b[3]+b[4] …+b[x]
第3行b[1]+b[2]+b[3]+b[4] …+b[x]
第4行b[1]+b[2]+b[3]+b[4] …+b[x]
.
.
.
第x行b[1]+b[2]+b[3]+b[4] …+b[x]
我们将上面看成一个矩阵
现在我们需要求的就是加粗部分
加粗部分=整个矩阵-非加粗部分
整个矩阵=(x+1)*(a[1]+a[2]+…a[x])
//上面x+1的因为多补了一行
非加粗部分=1 * b[1]+2 * b[2]+3 * b[3]+…x * b[x]
//这里就是多补一行的原因 让后这一部分变成i * b[i]的形式
加粗部分=(x+1)* (b[1]+b[2]+…b[x])-(1* b[1]+2* b[2]+3* b[3]+…x* b[x])=(x+1)(b的前缀和)-(ib[i]的前缀和)
那么我们只需要搞2个树状数组tr1(维护b)tr2(维护i*b[i])
每次logn时间查询差分数组前缀和 还有i*b[i]的前缀和
区间修改也就是改差分数组2个点就ok了
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
const int N=1e6+100;
LL tr1[N];//维护第差分数组b
LL tr2[N];//维护i *b[i]数组
int lowbit(int i)//取二进制最低位1
{
return i&-i;
}
void add(LL tr[],int i,LL x)//单点修改
{
for(;i<=n;i+=lowbit(i))
{
tr[i]+=x;
}
return ;
}
LL ask(LL tr[],int i)//区间查询
{
LL sum=0;
for(;i;i-=lowbit(i))
{
sum+=tr[i];
}
return sum;
}
LL fun(int i)//按照推的公式求值
{
return (i+1)*ask(tr1,i)-ask(tr2,i);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int m;
cin>>n>>m;
LL x1,x2;
x1=0;
for(int i=1;i<=n;i++)
{
cin>>x2;
add(tr1,i,x2-x1);
add(tr2,i,i*(x2-x1));
x1=x2;
}
while(m--)
{
int f;
cin>>f;
if(f==1)
{
int l,r;
LL d;
cin>>l>>r>>d;
add(tr1,l,d),add(tr1,r+1,-d);
add(tr2,l,l*d),add(tr2,r+1,(r+1)*(-d));
}
else
{
int r,l;
cin>>l>>r;
LL t=fun(r)-fun(l-1);
cout<<t<<endl;
}
}
return 0;
}