线段数求和模板

本文介绍了一种基于线段树的数据结构实现方法,重点在于如何高效地进行区间求和操作。通过递归构建和更新树结构,可以快速查询任意指定区间内的元素总和。

/*

上次实现了线段树上的求组区间最大值操作,现在来实现求区间和的操作,其实代码兵没哟修改多少

同理,拿走的童鞋请点个赞,谢谢

本文章是个人作品,可以转载,因为你知道的太多了,如果你心情好,带上地址吧,我叫WilliamCode

*/

#include<iostream>

#include<string.h>

#include<stdio.h>

#define lson id*2    //id的左子树编号

#define rson id*2+1    //id的右子树编号

using namespace std;

int ans;

//id是每个所在区间的编号

//l和r是id所代表区间的左右节点


int tre[2000700];

int a,b,c,d,n,m,k;

int push(int id)

{

    tre[id]=tre[lson]+tre[rson];//把子叶的值拿来更新父亲节点的值

    return 0;

}



int build(int id,int l,int r)   //树的初始化,必须的操作,虽然我也不知道为什么,好像是为了给每个点编号

{

    if (l>r)    return 0;//去除非法状态,以下每个子函数都要写

    if (l==r)

                {

                tre[id]=0;

                return 0;


                }

    int mid=(l+r)/2;

    build(lson,l,mid);//递归左右子树

    build(rson,mid+1,r);//注意是mid+1

    push(id);

}

int add(int id,int l,int r,int pos,int num)     //在编号为id的区间【l,r】中,在pos位置插入num

{

    if(l>r) return 0;//判断非法状态

    if (l==r && r==pos)     //如果到了最后一层,更新值,记得return掉

        {

        tre[id]+=num;

        return 0;

        }

    int mid=(l+r)/2;

    if (pos<=mid)//如果pos在当前区间中点的左边,则在左子树中递归,反之在右子树中递归

        {

            add(lson,l,mid,pos,num);

        }

    if (pos>=mid+1)

        {

        add(rson,mid+1,r,pos,num);

        }

    push(id);//下一层递归完成后对本层进行更新

}

int query(int id,int l,int r,int L,int R)

{

    if (l>r || L>R)    return 0; //非法状态

    if (l>=L && r<=R)

        {

        ans+=tre[id];

        return 0;//一定记得return,都则id会大的飞起

        }

    int mid=(l+r)/2;

    if (L<=mid)    //如果所查询区间和【l,r】的左半边有重合,则递归求左半边,右半边同理

        {

        query(lson,l,mid,L,R);

        }

    if (mid+1<=R)

        {

        query(rson,mid+1,r,L,R);

        }

    return 0;

}

int main()

{


/******************

使用方法

1,初始化: build(1,1,maxx) maxx是所开数组的长度或者题目要求的长度

2,给节点加值 add(1,1,maxx,pos,num)  pos是位置,num是数值

3,查询区间[L,R]的和   query(1,1,maxx,L,R)

******************/

build(1,1,1000);

int n,m,a,b;


cin>>n>>m;

for (int i=1;i<=n;i++)

        {

        scanf("%d%d",&a,&b);

        add(1,1,m,a,b);

        }

for (int i=1;i<=m;i++)

        {

        scanf("%d%d",&a,&b);

        ans=0;

        query(1,1,m,a,b);

        cout<<ans<<endl;

        }

}


### Python 线段模板代码实现 线段树是一种高效的据结构,用于处理区间查询更新问题。下面是一个基于给定参考资料的Python版线段模板代码[^1]。 #### 初始化函 初始化线段树时,通常会传入组`arr`作为输入,并构建相应的节点: ```python class SegmentTree: def __init__(self, arr): n = len(arr) self.n = n self.tree = [Node(0, 0, 0)] * (4 * n) # Node(lazy, l, r, v) def build(i, l, r): if l == r: self.tree[i] = Node(0, l, r, arr[l]) return mid = (l + r) // 2 build(2 * i, l, mid) build(2 * i + 1, mid + 1, r) self.tree[i] = Node( 0, l, r, self.tree[2 * i].v + self.tree[2 * i + 1].v ) build(1, 0, n - 1) ``` #### 更新函 当需要对某个区间的值进行批量增加时,可以使用懒惰传播来优化性能。这涉及到延迟计算直到必要为止: ```python def update_range(self, ql, qr, val): def putdown(i): self.tree[2 * i].lazy += self.tree[i].lazy self.tree[2 * i + 1].lazy += self.tree[i].lazy mid = (self.tree[i].l + self.tree[i].r) // 2 self.tree[2 * i].v += self.tree[i].lazy * (mid - self.tree[i].l + 1) self.tree[2 * i + 1].v += self.tree[i].lazy * (self.tree[i].r - mid) self.tree[i].lazy = 0 def modify(i, ql, qr, val): if self.tree[i].r < ql or self.tree[i].l > qr: return if ql <= self.tree[i].l and self.tree[i].r <= qr: length = self.tree[i].r - self.tree[i].l + 1 self.tree[i].v += length * val self.tree[i].lazy += val return putdown(i) modify(2 * i, ql, qr, val) modify(2 * i + 1, ql, qr, val) self.tree[i].v = self.tree[2 * i].v + self.tree[2 * i + 1].v modify(1, ql, qr, val) ``` #### 查询函 对于范围求和或其他类型的聚合操作,则可以通过递归方式遍历子节点并累积结果: ```python def query_sum(self, ql, qr): def getsum(i, ql, qr): if self.tree[i].r < ql or self.tree[i].l > qr: return 0 if ql <= self.tree[i].l and self.tree[i].r <= qr: return self.tree[i].v putdown(i) sum_left = getsum(2 * i, ql, qr) sum_right = getsum(2 * i + 1, ql, qr) return sum_left + sum_right return getsum(1, ql, qr) ``` 以上实现了基本的功能模块,具体应用可根据实际需求调整细节部分[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值