题面
给一个数列,需要满足以下两种操作:
①区间加x
②区间求和
数列规模N<=100000,操作数M<=100000
分析
树状数组基础:logN内区间求和,logN内单点修改
让树状数组维护差分数组,能做到单点求值, 区间修改。
让树状数组可以同时在区间查询+区间修改是对维护量进行变更。
从队友那里问到了如下等式
设di是差分数组,ai是原数组,即
a
1
=
d
1
a1=d1
a1=d1
a
2
=
d
1
+
d
2
a2=d1+d2
a2=d1+d2
a
3
=
d
1
+
d
2
+
d
3
a3=d1+d2+d3
a3=d1+d2+d3
…
a
n
=
d
1
+
d
2
+
d
3..
+
d
n
an=d1+d2+d3..+dn
an=d1+d2+d3..+dn
有如下关系
∑
i
=
1
n
a
i
=
(
n
+
1
)
a
n
−
∑
i
=
1
n
i
∗
c
i
\sum_{i=1}^{n}a_{i}=(n+1)a_{n}-\sum_{i=1}^{n}i*c_{i}
∑i=1nai=(n+1)an−∑i=1ni∗ci
可以自己验证一下
有了这个之后,可知要维护两个树状数组:
①单纯的差分数组,从而求
a
n
a_{n}
an
②数组
i
∗
c
i
i*c_{i}
i∗ci,从而能求出后面项的
∑
\sum
∑
代码
#include "cstdlib"
#include "iostream"
#include<iostream>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
#define lowbit(x) (x&(-x))//lowbit操作:x&(-x),取到最低位的1及其后的0
class Binary_Indexed_Tree {
private:
const static int MAXN = 100005;
long long C[MAXN];
public:
int N;//一共1~n
void add(int p, long long v)//单点加
{
while (p <= N)
{
C[p] += v;
p += lowbit(p);
}
}
long long query(int p)//查询从1到p的前缀和
{
long long ans = 0;
while (p)
{
ans += C[p];
p -= lowbit(p);
}
return ans;
}
}diff, idiff;//维护差分 和 i*差分di
class BIT_chunk {
public:
void add(int l, int r, long long v)//[l,r]内加v,即改变两端的差分值(两个单点修改)
{
diff.add(l, v);
idiff.add(l, l * v);
if (r < diff.N) { diff.add(r + 1, -v); idiff.add(r + 1, -v * (r + 1)); }
}
long long query(int l, int r)//根据公式,对两个差分分别计算区间和
{
return (r + 1) * diff.query(r) - idiff.query(r) - l * diff.query(l - 1) + idiff.query(l - 1);
}
void create(int *A, int s,int n)//数组,起点,长度
{
diff.N = n;
idiff.N = n;
diff.add(1, A[s]);
idiff.add(1, 1 * A[s]);
for (int i = s+1,j=2; i <= n; i++,j++)
{
diff.add(i, A[i] - A[i - 1]);
idiff.add(i, j * (A[i] - A[i - 1]));
}
}
}BIT;
int arr[100005];
int main()
{
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)cin >> arr[i];
BIT.create(arr, 1, n);
int op, k;
long long x, y;
for (int i = 0; i < m; i++)
{
cin >> op >> x >> y;
if (op == 1) { cin >> k;BIT.add(x, y, k); }
else { cout << BIT.query(x, y) << endl; }
}
return 0;
}