建树
- 父节点对应的权值是左右子树权值的和。
- 当节点的左右区间相同时,该节点为叶子节点。
递归建立代码:
ll a[N]; //值
struct node
{
ll l, r, sum;
}T[N*4];
// i 是根节点位置。l、r是区间大小
void build(ll i, ll l, ll r)
{
T[i].l = l;
T[i].r = r;
if (l == r) //如果到达叶子节点
{
T[i].sum = a[l]; //权值就是本身
return;
}
ll mid = (l+r)/2;
build(i *2, l, mid); //左子树
build(i *2 + 1, mid + 1, r); //右子树
T[i].sum = T[i *2].sum + T[i*2+1].sum; //父节点的权重等于左、右子树之和
}
单点修改,区间查询
区间查询
- 如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
- 如果这个区间的左儿子和目标区间有交集,那么搜索左儿子
- 如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
代码:
ll Search_(ll i, ll l, ll r) // i是根节点,l,r为目标区间
{
// 如果位置i对应的区间完全在目标区间内,就直接返回
if (l <=T[i].l && T[i].r<=r)
return T[i].sum;
//如果位置i对应的区间完全不在目标区间内,返回0
if (l > T[i].r || r < T[i].l)
return 0;
ll res = 0;
//与位置i的左子树区间有交集(条件是目标区间的r>=左子树的r)
if (l <= T[i * 2].r) res += Search_(i * 2, l, r);
if (r >= T[i *2+1].l) res += Search_(i * 2 + 1, l, r);
return res;
}
单点修改
思路:我们通过dis位置看哪些区间与其有交集,然后一直缩小区间,直到找到其对应的叶子节点,该叶子节点的值加上后,在递归回去过程修改经过的沿途的节点的值。
代码:
void add(ll i, ll dis, ll value)
{
if (T[i].l == T[i].r) //找到了
{
T[i].sum += value;
return;
}
//目标位置与i位置的左子树有交集,去左子树找
if (dis <= T[i * 2].r) add(i * 2, dis, value);
else //右子树
add(i * 2 + 1, dis, value);
T[i].sum = T[i * 2].sum + T[i *2+1].sum; //修改父节点的值
return;
}
区间修改,单点查询
在这里建树的时候要改变一个地方:
void build(ll i, ll l, ll r)
{
T[i].l = l;
T[i].r = r;
if (l == r) //如果到达叶子节点
{
T[i].sum = a[r]; //权值就是本身
return;
}
ll mid = (l+r)/2; //问题在这
build(i * 2, l, mid); //左子树
build(i * 2 + 1, mid + 1, r); //右子树
//T[i].sum = T[i * 2].sum + T[i * 2 + 1].sum; //父节点的权重等于左、右子树之和
T[i].sum = 0; //置为0
}
区间修改
- 如果这个区间被完全包括在目标区间里面,那么该位置的值修改
- 如果这个区间的左儿子和目标区间有交集,那么搜索左儿子
- 如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
代码:
void add(ll i, ll l, ll r, ll value)
{
if (l <= T[i].l && T[i].r <= r) //完全在目标范围内
{
T[i].sum += value;
return;
}
if (l > T[i].r || r < T[i].l)
return;
if (l <= T[i * 2].r) //看左子树是否有交集
add(i * 2, l, r, value);
if (r >= T[i * 2 + 1].l)//看右子树是否有交集
add(i * 2 + 1, l, r, value);
}
单点查询
dis 在哪里我们就往哪里跑,然后将沿途的值加起来。
void query(ll i,ll dis)
{
ans += T[i].sum;//一路加上来
if (T[i].l == T[i].r)
return;
if (dis <= T[i * 2].r) //看左子树是否有交集
query(i * 2,dis);
if (dis >= T[i * 2 + 1].l)//看右子树是否有交集
query(i * 2+1, dis);
}
区间修改,区间查询
建树,多了一个lazy 标记
struct node
{
int l, r;
int lazy, sum;
}T[N*4];
pushdown
函数:
void push_down(int id)
{
if(tree[id].lazy)
{
tree[id*2].update(tree[id].lazy);
tree[id*2+1].update(tree[id].lazy);
tree[id].lazy=0;
}
}
void push_up(int id)
{
tree[id].s=tree[id*2].s+tree[id*2+1].s;
}
区间修改:
void pushdown(int i)
{
if (T[i].lazy!=0)//有标记
{
//左右儿子分别加上分节点的lazy
T[i * 2].lazy += T[i].lazy;
T[i * 2 + 1].lazy += T[i].lazy;
//左右儿子的区间和加上
ll mid = (T[i].l + T[i].r) / 2;
T[i * 2].sum += T[i].lazy * (mid - T[i * 2].l + 1); //
T[i * 2 + 1].sum += T[i].lazy * (T[i*2+1].r-mid);
//用完清0
T[i].lazy = 0;
}
return;
}
区间查询:
ll query(int id,int l,int r)
{
int L=tree[id].l,R=tree[id].r;
ll res=0;
if(R<l||L>r)
return 0;
if(l<=L&&R<=r)
return tree[id].s;
push_down(id);
if(tree[id*2].r>=l)
res+=query(id*2,l,r);
if(tree[id*2+1].l<=r)
res+=query(id*2+1,l,r);
return res;
}
完整代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5;
int n,m,a[N],k,l,r,x;
struct node
{
int l,r;
ll s,lazy;
void update(ll k)
{
s+=(r-l+1)*k;
lazy+=k;
}
}tree[4*N];
void push_down(int id)
{
if(tree[id].lazy)
{
tree[id*2].update(tree[id].lazy);
tree[id*2+1].update(tree[id].lazy);
tree[id].lazy=0;
}
}
void push_up(int id)
{
tree[id].s=tree[id*2].s+tree[id*2+1].s;
}
void build(int id,int l,int r)
{
tree[id].l=l;
tree[id].r=r;
if(l==r)
{
tree[id].s=a[l];
return;
}
int mid=(l+r)/2;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
push_up(id);
}
void update(int id,int l,int r,int k)
{
int L=tree[id].l,R=tree[id].r;
if(R<l||L>r)
return;
if(l<=L&&R<=r)
{
tree[id].update(k);
return;
}
push_down(id);
if(tree[id*2].r>=l)
update(id*2,l,r,k);
if(tree[id*2+1].l<=r)
update(id*2+1,l,r,k);
push_up(id);
}
ll query(int id,int l,int r)
{
int L=tree[id].l,R=tree[id].r;
ll res=0;
if(R<l||L>r)
return 0;
if(l<=L&&R<=r)
return tree[id].s;
push_down(id);
if(tree[id*2].r>=l)
res+=query(id*2,l,r);
if(tree[id*2+1].l<=r)
res+=query(id*2+1,l,r);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
scanf("%d",&k);
if(k==1)
{
scanf("%d%d%d",&l,&r,&x);
update(1,l,r,x);
}
else
{
scanf("%d%d",&l,&r);
printf("%lld\n",query(1,l,r));
}
}
return 0;
}
版子二
#include<cstdio>
using namespace std;
const int maxn = 100005;
struct node {
int l, r, sum, lazy;
}tr[maxn << 2];
void pushup(int m) {
tr[m].sum = tr[m << 1].sum + tr[m << 1 | 1].sum;
}
void build(int m, int l, int r) {
tr[m].l = l;
tr[m].r = r;
tr[m].lazy = 0;
if (l == r) {
tr[m].sum = 1;//初始的值,根据题目来赋值
return;
}
int mid = (l + r) >> 1;
build(m << 1, l, mid);
build(m << 1 | 1, mid + 1, r);
pushup(m);
}
void pushdown(int m) {
if (tr[m].lazy) { //存在标记
//左右儿子继承
tr[m << 1].lazy = tr[m].lazy;
tr[m << 1 | 1].lazy = tr[m].lazy;
//左右子树求和加起来
tr[m << 1].sum = tr[m].lazy * (tr[m << 1].r - tr[m << 1].l + 1);
tr[m << 1 | 1].sum = tr[m].lazy * (tr[m << 1 | 1].r - tr[m << 1 | 1].l + 1);
//用完清空
tr[m].lazy = 0;
}
}
void updata(int m, int l, int r, int val) {
if (tr[m].l == l && tr[m].r == r) {
tr[m].lazy = val;//根据题干,这里是修改,不是加起来
tr[m].sum = val * (r - l + 1);
return;
}
pushdown(m);
int mid = (tr[m].l + tr[m].r) >> 1;
if (r <= mid) updata(m << 1, l, r, val);
else if (l > mid) updata(m << 1 | 1, l, r, val);
else {
updata(m << 1, l, mid, val);
updata(m << 1 | 1, mid + 1, r, val);
}
pushup(m); //记得在后边再来一次
}
int query(int m, int l, int r) {
if (tr[m].l == l && tr[m].r == r) {
return tr[m].sum;
}
pushdown(m);
int mid = (tr[m].l + tr[m].r) >> 1;
int temp;
if (r <= mid) temp = query(m << 1, l, r);
else if (l > mid) temp = query(m << 1 | 1, l, r);
else temp = query(m << 1, l, mid) + query(m << 1 | 1, mid + 1, r);
return temp;
}
int main() {
int T, N, Q, a, b, x;
scanf("%d", &T);
for (int k = 1; k <= T; k++) {
scanf("%d%d", &N, &Q);
build(1, 1, N);
while (Q--) {
scanf("%d%d%d", &a, &b, &x);
updata(1, a, b, x);
}
printf("Case %d: The total value of the hook is %d.\n", k, query(1, 1, N));
}
return 0;
}