线段树模板

6 篇文章 2 订阅

建树

在这里插入图片描述

  • 父节点对应的权值是左右子树权值的和。
  • 当节点的左右区间相同时,该节点为叶子节点。

递归建立代码:

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落春只在无意间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值