hdu 1166 线段树 单点修改 + 询问区间求和 (线段树模板)

题目来源:

http://acm.hdu.edu.cn/showproblem.php?pid=1166

借鉴大牛李的线段树模板

const int Max_N=50100;
int sum[Max_N<<2] ; // 线段树中每个点需要维护的区间和
int x[Max_N]; // 输入数组
//从下向上更新值
void push_up(int root){
    sum[root] = sum[root<<1] + sum[root<<1|1]
}
// 建树,从根节点出发,根节点从1开始
void make_tree(int L, int R, int root){
    if(L == R){
        sum[root] = x[L];
        return ;
    }
    int mid=(L + R)>>1;
    make_tree(L, mid, root<<1 );
    make_tree(mid+1, R, root<<1|1);
    push_up(root); // 先递归,后从下往上更新区间和值
}
// 将id值增加c, 调入的是整棵树的区间和根节点1
void update(int id, int c, int L, int R, int root)
{
    if(L == R){
        sum[root]+=c;
        return ;
    }
    int mid=(L + R)>>1;
    if(id<=mid )
        update(id, c, L, mid , root<<1);
    else
        update(id,c, mid+1, R, root<<1|1);
    push_up(root); // 更新是从上到下寻找需要更改的节点,然后从下往上地更新这些节点的区间和值
}
// 查询区间[l,r] 的 区间和值,在递归时不更改[l,r]区间的写法
更一般(适应性越强)的写法

int query(int l, int r, int L, int R, int root){
  if(l<=L && R <= r)
    return sum[root];
  int mid=(L + R)>>1;
  if(mid >= r)
    return query(l,r,L, mid, root<<1);
  else if(mid < l)
    return query(l,r,mid+1, R, root<<1|1);
  else
    return query(l,r,mid+1, R, root<<1|1)  +  query(l , r, L, mid, root<<1);
}

// 查询如果仅是加法, 就可以套这个模板
int query(int l, int r, int L, int R, int root)
{
    if(l <= L && R <= r)
        return sum[root];
    int mid=(L + R)>>1;
    int ans=0;
    if(l<= mid)
        ans+=query(l, r, L, mid, root<<1);
    if(r>mid)
        ans += query(l, r, mid+1, R, root<<1|1);
    return ans;
}

 

 

 

 

代码如下:

#include 
#include 
#include 
#include 
#include 
#include <string>
#include <string.h>
#include
#include 
#include 
#include 
#include <set>
#include 
#include 
#include 
#include 
using namespace std ;
typedef long long LL ;

// 线段树 单点修改,区间求和
#define left(x) (x<<1)
#define right(x) ((x<<1) + 1)
const int maxn=50005;
// 此线段树需要维护的附加信息是num,指的是每个节点所代表区间【l,r】之和
struct tree{
    int l,r,num;
};

tree anode[maxn<<2]; // 线段树的节点估计为2*maxn 个
int  data[maxn];
// 附加信息的向上更新只与该节点或者节点的子节点有关,故可以扩展线段树
void pushup(int n){
    anode[n].num=anode[left(n)].num + anode[right(n)].num;
    return ;
}// 向上更新
// 建线段树,从根节点1开始,调用为 build(1,n,1)
void build(int l, int r, int n){
    int mid;
    mid=(l+r)>>1;
    if(l==r){
        anode[n].l=l;
        anode[n].r=r;
        anode[n].num=data[l];
        return ;
    }
    anode[n].l=l;
    anode[n].r=r;
    build(l, mid, left(n));
    build(mid+1, r, right(n));
    pushup(n);// 先递归,后从下往上更新区间和值
}
// 线段树更新,从根节点开始, 调用为 update(aim_id, new_num, 1)
void update(int aim_id, int new_num, int n){
    if(anode[n].l == aim_id && anode[n].r== aim_id ){
        anode[n].num += new_num;
        return ;
    }
    if(anode[ left(n) ].l <= aim_id && anode[left(n)].r >=aim_id)
        update(aim_id, new_num, left(n));
    else update(aim_id, new_num, right(n));
    pushup(n);// 先递归需要更改的节点,然后从下往上的更新区间和值
    return ;
}
// 线段树查询,从根节点开始, 调用为 query(l,r, 1)
int query(int l, int r, int n){
    if(anode[n].l ==l && anode[n].r ==r)
        return anode[n].num;
    int mid;
    mid= (anode[n].l + anode[n].r)>>1;
    if(mid >=r) return query(l,r,left(n));
    else if(mid< l) return query(l,r,right(n));
    else return query(l,mid,left(n)) + query(mid+1, r, right(n));
}

int  main(){
    int ca,k=1;
    scanf("%d",&ca);
    while(ca--){
            int n;
        scanf("%d",&n);
    for(int i=1 ; i<=n; i++)
        scanf("%d",&data[i]);
    build(1,n,1);
    printf("Case %d:\n",k++);
    char od[12];
    while(1){
        scanf("%s",od);
        if(od[0] == 'E') break;
        int a,b;
        scanf("%d%d",&a,&b);
        if(od[0] == 'A')
            update(a,b,1);
        else if(od[0] == 'S')
            update(a,-b,1);
        else
            printf("%d\n",query(a,b,1));
    }
    }
     return 0 ;
}

 

 

转载于:https://www.cnblogs.com/zn505119020/p/3653592.html

你可能感兴趣的:(hdu 1166 线段树 单点修改 + 询问区间求和 (线段树模板))