线段树之区间求和 POJ 3468 线段树入门

题目就是要经常查询某短区间的和,而且要经常修改一段区间的值即同是把这个区间的每个元素都加上一数

这中要求线段树就可以实现修改和查询的时间都是logN的

下面是代码,注释可以参考一下,希望可以解释清楚,第一次学可能不好懂,但出几个简单的数据模拟一下就大概懂了

#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;
#define maxn 100005
struct point
{
    int left,right;
    long long sum;//初始化的时候sum是当前这个区间的所有元素的和 其实sum究竟表示什么很难说清楚我感觉
    //非要说的话他就是 原始的left right闭区间元素的和再加上后来在把某段区间的元素都增加一定值的时候在执行update时二分出的区间递归到能够小于或等于这个区间时的增加的和
    //是不很拗口????反正我觉得是的,这个不用图很难表述清楚
    long long jia;//对当前整个区间每个元素应该增加的值
}node[4*maxn];
int score[maxn];//原始的数据
void buildtree(int left,int right,int u)//初始化的建树函数
{
    node[u].left=left;//设置区间的左端点
    node[u].right=right;//设置区间的右端点
    node[u].jia=0;//初始化整个区间每个元素都要增加的值为零
    if(left==right) node[u].sum=score[left];//如果区间的长度为一则区间和就为它所存的元素
    else//反之则二分递归下去
    {
        buildtree(left,(left+right)/2,2*u);
        buildtree(((left+right)/2)+1,right,2*u+1);
        node[u].sum=node[2*u].sum+node[2*u+1].sum;//当前区间的和为他左右的子区间的和
    }
};
void updata(int l,int r,int val,int u)//更新区间,把区间l到r的元素都加上val
{
    node[u].sum+=(long long)(r-l+1)*(long long)val;//区间的和增加
    int mid=(node[u].left+node[u].right)/2;//二分当前的区间
    if(node[u].left==l&&node[u].right==r)//如果要跟新的区间与现在的区间是同一个区间就把每个元素要加的值加在jia上以便需要统计这段区间的和的时候用
        node[u].jia+=(long long)val;
    else if(r<=mid)//二分递归,
        updata(l,r,val,2*u);
    else if(l>mid)
        updata(l,r,val,2*u+1);
    else
    {
        updata(l,mid,val,2*u);
        updata(mid+1,r,val,2*u+1);
    }
}
int n,m;char temp;
long long query(int left,int right,int u)//查询区间left到right区间的所有元素的和
//left 和 right为要查找的区段和的区间
{
    int mid=(node[u].left+node[u].right)/2;//二分当前区间的中值
    long long t=(long  long)(right-left+1)*(long long )node[u].jia;//t 也是一个不好描述的东西
    //因为node[u].jia是 node[u].left 和node[u].right 内每个元素应该增加的值 所以要求 left到right这个包含在node[u].left 和node[u].right
    //内的区间的元素的和当然要加上t了即(right-left+1)*node[u].jia 要注意可能会溢出
    if(node[u].left==left&&node[u].right==right)//如果要求和的区间恰好的等于当前节点的区间就返回这个难懂的node[u].sum
       return node[u].sum;
    if(right<=mid)//在左区间就去左区间去找
        return query(left,right,2*u)+t;
    if(left>mid)//到右区间去找
        return query(left,right,2*u+1)+t;
    return  query(left,mid,2*u)+query(mid+1,right,2*u+1)+t;//否则就说明left到right区间横跨了左右2个子区间,就要把left到right的区间用mid分成2个部分
}
int main()
{
  //freopen("in.txt","r",stdin);
    while(scanf("%d%c%d%c",&n,&temp,&m,&temp)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%d%c",&score[i],&temp);
        char c;int s,e,h;
        buildtree(1,n,1);
        for(int i=0;i<m;i++)
        {
           scanf("%c%c",&c,&temp);
            if(c=='C')
            {
                scanf("%d%c%d%c%d%c",&h,&temp,&s,&temp,&e,&temp);
                updata(h,s,e,1);
            }
            if(c=='Q')
            {
                scanf("%d%c%d%c",&s,&temp,&e,&temp);
                printf("%I64d\n",query(s,e,1));
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值