POJ3468 区间成段增减

POJ3468 A Simple Problem with Integers
思考:本题节点要存哪些信息?只存该区间的数的和,行不行?
NO! NO!
分析:只存和,会导致每次加数的时候都要更新到叶子节点,速度太慢,这是必须要避免的。本题树节点结构:
struct node{
        int left,right;//区间起点和终点
        long long sum;//原来的和
        long long add;//增量的累加
}//本节点区间的和实际上是sum+add*(right-left+1)

思路:
在增加时,如果要加的区间正好覆盖一个节点,则增加其节点的add值,不再往下走,否则要更新sum,再将增量往下传。
在查询时,如果待查区间不是正好覆盖一个节点,就将节点的add往下带,然后将add代表的所有增量累加到sum上,然后将add清0,接下来再往下查询

#include<cstdio>
struct node{
    int left,right;//区间起点和终点
    long long sum;//原来的和
    long long add;//节点携带的增量
}tree[100001*4];
int c;
void Build(int l,int r,int cur){
    tree[cur].left=l;
    tree[cur].right=r;
    tree[cur].add=0;//初始增量为0
    if(l==r){
        scanf("%lld",&tree[cur].sum);
        return ;
    }
    int mid=(tree[cur].left+tree[cur].right)/2;
    Build(l,mid,cur*2);
    Build(mid+1,r,cur*2+1);
    tree[cur].sum=tree[cur*2].sum+tree[cur*2+1].sum;
}
void Add(int cur,int m){
    if(tree[cur].add){//如果有增量
        tree[cur*2].add+=tree[cur].add;//将增量往给子节点
        tree[cur*2+1].add+=tree[cur].add;
        tree[cur*2].sum+=tree[cur].add*(m-m/2);//同时更行子节点的和(同样遵循着和为sum+add*(r-l+1))
        tree[cur*2+1].sum+=tree[cur].add*(m/2);
        tree[cur].add=0;//传完后将自己的增量清0
    }
}
void Update(int l,int r,int cur){
    if(l==tree[cur].left&&r==tree[cur].right){//给定区间刚好覆盖一个节点时
        tree[cur].add+=c;//则增加节点的增量值
        tree[cur].sum+=c*(r-l+1);//该节点的和更新为sum+c*(r-l+1)
        return ;//然后就不再往下走了
    }
    Add(cur,tree[cur].right-tree[cur].left+1);//不完全覆盖时就往下传递增量
    int mid=(tree[cur].left+tree[cur].right)/2;
    if(r<=mid)Update(l,r,cur*2);
    else if(l>mid)Update(l,r,cur*2+1);
    else{
        Update(l,mid,cur*2);
        Update(mid+1,r,cur*2+1);
    }
    tree[cur].sum=tree[cur*2].sum+tree[cur*2+1].sum;
}
long long Query(int l,int r,int cur){
    if(l==tree[cur].left&&r==tree[cur].right){//给定区间刚好覆盖一个节点
        return tree[cur].sum;//就返回节点的sum
    }
    Add(cur,tree[cur].right-tree[cur].left+1);//不完全覆盖时就往下传递增量
    int mid=(tree[cur].left+tree[cur].right)/2;
    if(r<=mid)return Query(l,r,cur*2);
    else if(l>mid)return Query(l,r,cur*2+1);
    else return Query(l,mid,cur*2)+Query(mid+1,r,cur*2+1);
}
int main(){
    int n,q,l,r;
    char ch;
    scanf("%d%d",&n,&q);
    Build(1,n,1);
    while(q--){
        getchar();
        scanf("%c%d%d",&ch,&l,&r);
        if(ch=='Q')printf("%lld\n",Query(l,r,1));
        else{
            scanf("%d",&c);
            Update(l,r,1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值