树状数组3区间查询区间修改

Description

要求使用树状数组完成区间之和查询,区间加上某一相同数值的操作。

Solution

树状数组是用来单点加,查前缀和的。若要实现区间加,可以将原数列差分,然后在l位置处+val,在r+1处-val,这时要查询的就成了在差分数列上前缀和的前缀和,即二阶前缀和。

Sum=i=1nj=1ixj

对于第i位来讲,它的值是这样组成的。

(i0)x1+(i1)x2+(i2)x2++(i(i1))xi

化简一下

ij=1ixjj=1i(j1)xj

再化简一下

ij=1ixjj=1ijxj+j=1ixj

此时就比较明了了,现在就是是要求 i=1nxi 以及 i=1nixi这两个前缀和。第二个其实和第一没有什么本质区别,就是在给某一位加上val时,给第二个的前缀和加上i*val

#include<iostream>
using namespace std;
const int N = 1e6+10;
int n;
int A[N];
long long tr1[N] , tr2[N];
inline int lowbit(int x) { return x & (-x); }
inline void Add(int pos , int val)
{
long long val2 = 1ll * val * pos;
while(pos <= n) { tr1[pos] += val; tr2[pos] += val2; pos += lowbit(pos); }
}
inline long long Ask1(int pos)
{
long long Ans = 0;
while(pos) { Ans += tr1[pos]; pos -= lowbit(pos); }
return Ans;
}
inline long long Ask2(int pos)
{
long long Ans = 0;
while(pos) { Ans += tr2[pos]; pos -= lowbit(pos); }
return Ans;
}
inline long long Ask(int l , int r)
{
long long res1 = Ask1(r) * (r + 1) - Ask1(l-1) * l;
long long res2 = Ask2(r) - Ask2(l-1);
return res1 - res2;
}
int main()
{
int Q , op , l , r , x;
cin >> n >> Q;
for(int i = 1 ; i <= n ; ++i)
cin >> A[i];
for(int i = n ; i >= 2 ; --i)
A[i] = A[i] - A[i-1];
for(int i = 1 ; i <= n ; ++i)
Add(i , A[i]);
while(Q--)
{
cin >> op >> l >> r;
if(op == 1)
{
cin >> x;
Add(l , x); Add(r + 1 , -x);
}
else
cout << Ask(l , r) << '\n';
}
return 0;
}
posted @   沙野博士  阅读(264)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示