版权声明: https://blog.csdn.net/leelitian3/article/details/81915327
题目:https://www.luogu.org/problemnew/show/P3372
线段树(含区间修改)
#include<iostream>
using namespace std;
#define LEFT(x) (x << 1)
#define RIGHT(x) ((x << 1) | 1)
long long seg[100005 * 4];
long long delta[100005 * 4];
void pushUp(int a) //由2个子树更新root
{
seg[a] = seg[LEFT(a)] + seg[RIGHT(a)];
}
void build(int root, int low, int high)
{
if(low == high)
{
cin >> seg[root]; //输入
return;
}
int mid = (low + high) >> 1;
build(LEFT(root), low, mid); //递归建树
build(RIGHT(root), mid + 1, high);
pushUp(root);
}
void func(int a, int l, int h, int add)
{
seg[a] += (h - l + 1) * add;
delta[a] += add;
}
void pushDown(int a, int l, int h)
{
if(delta[a] == 0) return;
int mid = (l + h) >> 1;
func(LEFT(a), l, mid, delta[a]);
func(RIGHT(a), mid + 1, h, delta[a]);
delta[a] = 0;
}
void update(int root, int low, int high, int l, int h, int add)
{
if(l <= low && high <= h) //当前区间[low,high]被包含在查询区间[l,r]内部
{
seg[root] += add * (high - low + 1);
delta[root] += add;
return;
}
pushDown(root, low, high);
int mid = (low + high) >> 1;
if(l <= mid) //区间左边与[l,h]有交集
update(LEFT(root), low, mid, l, h, add);
if(h > mid)
update(RIGHT(root), mid + 1, high, l, h, add);
pushUp(root);
}
long long getSum(int root, int low, int high, int l, int h)
{
if(l <= low && high <= h) //当前区间[low,high]被包含在查询区间[l,r]内部
return seg[root];
pushDown(root, low, high);
long long res = 0;
int mid = (low + high) >> 1;
if(l <= mid) //区间左边与[l,h]有交集
res += getSum(LEFT(root), low, mid, l, h);
if(h > mid)
res += getSum(RIGHT(root), mid + 1, high, l, h);
return res;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n, m, op, a, b, c;
cin >> n >> m;
build(1, 1, n);
while(m--)
{
cin >> op;
if(op == 1)
{
cin >> a >> b >> c;
update(1, 1, n, a, b, c);
}
else if(op == 2)
{
cin >> a >> b;
cout << getSum(1, 1, n, a, b) << "\n";
}
}
return 0;
}
树状数组(查分+区间修改)
来介绍一下差分
设数组a[]={1,6,8,5,10},那么差分数组b[]={1,5,2,-3,5}
也就是说b[i]=a[i]-a[i-1];(a[0]=0;),那么a[i]=b[1]+....+b[i];(这个很好证的)。
假如区间[2,4]都加上2的话
a数组变为a[]={1,8,10,7,10},b数组变为b={1,7,2,-3,3};
发现了没有,b数组只有b[2]和b[5]变了,因为区间[2,4]是同时加上2的,所以在区间内b[i]-b[i-1]是不变的.
所以对区间[x,y]进行修改,只用修改b[x]与b[y+1]:
b[x]=b[x]+k;b[y+1]=b[y+1]-k;
维护查分数组的后缀和
#include <iostream>
using namespace std;
#define lowbit(x) ((x)&(-x))
long long n, m, op, a, b, d;
long long c[500005]; //c[i]保存了Σ([i-lowbit(i)+1,i])
void update(long long x, long long add) //x+lowbit(x)即为其父节点
{
while(x <= n)
{
c[x] += add;
x += lowbit(x);
}
}
long long getSum(long long x) //如求1~6,此时x=6
{
long long res = 0; //(110) = (10) + (100)
while(x)
{
res += c[x];
x -= lowbit(x);
}
return res;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
a = b = 0;
cin >> n >> m;
for(long long i = 1; i <= n; ++i)
{
cin >> b;
update(i, b - a);
a = b;
}
while(m--)
{
cin >> op;
if(op == 1)
{
cin >> a >> b >> d;
update(a, d);
update(b + 1, -d);
}
else if(op == 2)
{
cin >> a;
cout << getSum(a) << "\n";
}
}
return 0;
}