acm专题学习之线段树(一)线段树单点更新+求区间和 HDU - 1166

题意:给你n个点,告诉你每个点的值,然后有四种询问:增加其中一个点的值,增加其中一个点值,求一个区间的和,还有一个就是代表结束。

条件:1 单点更新

            2 区间求和

思路:简单的线段树基本操作

扩展:

线段树:它将每个区间划分成一些简单的区间,每个区间对应线段树中的一个节点。对于线段树中每一个非叶子节点[a,b],它的左子树区间表示为[a,(a+b)/2],右子树的区间表示为[(a+b)/2,b]。树为平衡二叉树,最后子节点数目为n。

作用:主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。

结合这题来理解:

(1)线段树的储存:用数组来存储,数组的大小一般开为总数的4倍。因为未优化的空间复杂度为2n但是为了防止数组越界一般会开4倍,有时会通过离散化来优化。

(2)线段树的建立:知道了大小遍历线段树,然后叶子节点的区间和就等于对应数字,最后递归完叶子,再让父节点等于叶子节点的和。

(3)线段树的单点修改:遍历递归到对应的单点然后进行修改,最后递归完叶子,再让父节点等于叶子节点的和。(这里递归到子叶要进行剪枝,就是范围判断一下)

(4)线段树的区间输出:递归同时进行范围的剪枝,然后递归到包含有的区间求和输出就好了。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn=50001;
int sum_tree[maxn<<2];
int array_tree[maxn];
void build_tree(int node,int fst,int lst)//线段树的建立
{
    if(fst==lst)//递归到子叶
    {
        sum_tree[node]=array_tree[fst];//子叶的区间和等于其本身
        return ;
    }
    int mid=(fst+lst)/2;
    build_tree(2*node,fst,mid);//左子树
    build_tree(2*node+1,mid+1,lst);//右子数
    sum_tree[node]=sum_tree[2*node+1]+sum_tree[2*node];//递归完左右,父节点等于其和
}
void updata_tree(int node,int fst,int lst,int val,int add)//单点修改
{
    if(fst==lst)//递归到单点
    {
        sum_tree[node]+=add;//单点修改
        return ;
    }
    int mid=(fst+lst)/2;
    if(val<=mid)//范围的剪枝
        updata_tree(node*2,fst,mid,val,add);
    else
        updata_tree(node*2+1,mid+1,lst,val,add);
    sum_tree[node]=sum_tree[node*2]+sum_tree[node*2+1];//递归完左右,父节点等于其和
}
int query_tree(int node,int fst,int lst,int val_fst,int val_lst)//区间的查找
{
    if(val_fst>lst||val_lst<fst) return 0;//如果范围不符合规范
    if(val_fst<=fst&&val_lst>=lst)//如果包含了这个范围
        return sum_tree[node];
    int ans=0;
    int mid=(fst+lst)/2;
    ans+=query_tree(node*2,fst,mid,val_fst,val_lst);
    ans+=query_tree(node*2+1,mid+1,lst,val_fst,val_lst);
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    int t1=0;
    while(t--)
    {
        printf("Case %d:\n",++t1);
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&array_tree[i]);
        build_tree(1,0,n-1);
        while(1)
        {
            string s;
            int i,j;
            cin>>s;
            if(s=="Add")
            {
                cin>>i>>j;
                updata_tree(1,0,n-1,i-1,j);
            }
            else if(s=="Sub")
            {
                cin>>i>>j;
                updata_tree(1,0,n-1,i-1,-j);
            }
            else if(s=="Query")
            {
                cin>>i>>j;
                printf("%d\n",query_tree(1,0,n-1,i-1,j-1));
            }
            else if(s=="End")
                break;
        }
    }
    return 0;
}

寄语:1 加油,现在开始一个专题一个专题的攻略,争取暑假有进步

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值