本题链接:用户登录
题目:
样例:
|
|
思路:
树状数组的区间修改,区间查询,操作起来有点繁琐了些,但还是可以理解的。
这里区间修改,我们还是需要需要通过差分来进行区间修改的。
不同的是,在于区间查询这一块。
我们先模拟一遍区间修改后,差分数组 dif,原数组 a
得: 差分数组的前缀和,就是获得当前原数组的数值
dif[1] = a[1]
dif[2] = a[2] - a[1]
dif[3] = a[3] - a[2]
dif[4] = a[4] - a[3]
...
...
dif[n] = a[n] - a[n-1]
而我们需要求的是区间和,所以当我们设前缀和数组为 sum可知
sum[1] = dif[1];
sum[2] = (dif[2] + dif[1]) + sum[1];
sum[3] = (dif[3] + dif[2] + dif[1]) + sum[2];
sum[4] = (dif[4] + dif[3] + dif[2] + dif[1]) + sum[3];
...
sum[n] = (dif[n] + dif[n-1] + ... + dif[1]) + sum[n-1];
所以,当我们区间查询区间求和的时候
可以得出,我们区间求和,相应的差分求和的求和具有一定的规律性。
此时我们将其化为差分,可以得知,减去剩余的 dif[] 随即递增的累乘
difSum[1] = sum[1];
difSum[2] = sum[2] - sum[1];
difSum[3] = sum[3] - sum[2];
difSum[4] = sum[4] - sum[3];
...
difSum[n] = sum[n] - sum[n-1];
difSum[n] = (dif[n] + dif[n - 1] + ... + dif[1])*(n + 1) -
(dif[1] * 1 + dif[2] * 2 + ... + dif[n] * n);
所以我们需要两个差分数组来进行区间修改以及区间查询。
一个 dif 为 正常的 (dif[n] + dif[n - 1] + ... + dif[1]) 的前缀和差分数组;
一个 p_dif 为 (dif[1] * 1 + dif[2] * 2 + ... + dif[n] * n)的前缀和差分数组;
还有前缀和函数 sum
最后通过前缀和相减,就可以获得区间和了
操作函数如下:
|
|
|
|
|
代码详解如下:
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#define endl '\n'
#define int long long
#define YES puts("YES")
#define NO puts("NO")
#define umap unordered_map
#define All(x) x.begin(),x.end()
#pragma GCC optimize(3,"Ofast","inline")
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;
inline int lowbit(int x){return (x&(-x));} // 定义 lowbit()
const int N = 2e6 + 10;
int n,m;
int dif[N],p_dif[N]; // 定义 dif 原差分数组,减去的剩余差分数组 p_dif
// 单点修改函数
inline void Add_pos(int tr[],int pos,int x)
{
for(int i = pos;i <= n + 1;i+=lowbit(i)) tr[i] += x;
}
// 获取前缀和函数
inline int getSum(int tr[],int pos)
{
int res = 0;
for(int i = pos;i;i-=lowbit(i)) res += tr[i];
return res;
}
// 区间修改加值
inline void Add_section(int l,int r,int x)
{
// 差分加值 dif
Add_pos(dif,l,x);
Add_pos(dif,r+1,-x);
// 差分加值 p_dif
Add_pos(p_dif,l,l*x);
Add_pos(p_dif,r+1,(r+1)*(-x));
}
// 获取 Sum 的前缀和
inline int get_difSum(int pos)
{
// 求 sum 的前缀和函数
return getSum(dif,pos)*(pos + 1) - getSum(p_dif,pos);
}
// 获取区间前缀和
inline int Ask_section(int l,int r)
{
return get_difSum(r) - get_difSum(l-1);
}
inline void solve()
{
cin >> n >> m;
for(int i = 1,r = 0,a,x;i <= n;++i)
{
cin >> a; // 输入原数组 初始值
x = a - r; // 获取差分值
Add_pos(dif,i,x); // 插入差分值
Add_pos(p_dif,i,i*x); // 插入需减掉的剩余差分值
r = a; // 记忆化上一个元素
}
while(m--)
{
int op;
cin >> op;
if(op == 1)
{
int l,r,x;
cin >> l >> r >> x;
Add_section(l,r,x); // 区间修改
}else
{
int l,r;
cin >> l >> r;
cout << Ask_section(l,r) << endl; // 区间查询
}
}
}
signed main()
{
// freopen("a.txt", "r", stdin);
IOS;
int _t = 1;
// cin >> _t;
while (_t--)
{
solve();
}
return 0;
}