POJ3468 A Simple Problem with Integers(线段树 lazy思想 区间查询更新)

A Simple Problem with Integers
Time Limit: 5000msMemory Limit: 131072KB 64-bit integer IO format: %lld Java class name: Main
Submit Status PID: 7955
You have N integers, A1, A2, … , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
“C a b c” means adding c to each of Aa, Aa+1, … , Ab. -10000 ≤ c ≤ 10000.
“Q a b” means querying the sum of Aa, Aa+1, … , Ab.

Output
You need to answer all Q commands in order. One answer in a line.

Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
Sample Output
4
55
9
15
Hint
The sums may exceed the range of 32-bit integers.

讲道理。。这道题当时卡了我很久,因为不知道lazy这个东西。。。
唉缺课就是不好o(`ω´ )o

然后但是知道了lazy之后写,还是卡了窝很久呜呜呜
区间查询的复杂度仍然是log2N的,但是区间修改如果仍然是从上到下每个区间都要更新的话,复杂度就很高了。(eg.[l,r]是需要更新的区间,按着单点更新的方式,就会从上到下所有[l,r]的子区间都要更新,但这样复杂度比O(n)还高,得不偿失)

但是我们发现其实没有必要每个区间都更新。因为如果要更新的区间刚好是某个节点的边界的话,我们只需要记录下来这个区间的增量是多少就可以了,并不需要接着向下更新。
如果要查询的这个区间是 我们记录了inc值的节点的子区间,那么我们就只需要把这个inc值沉降给儿子们,然后这个儿子区间真实是sum=sum+inc(r-l+1)这个值。
这样复杂度就又变回了log2n=-=

这里的pushdown函数就是一个沉降inc值的过程。
这就是一个lazy 的思想,也是一个继承的思想。(从父亲那里继承一些东西,这样可以少做很多事情)

int pushdown(int mid,int v){

    if(tree[v].inc==0) return 0;    
    tree[mid].inc+=tree[v].inc;
    tree[mid+1].inc+=tree[v].inc;
    tree[mid].sum+=tree[v].inc*(tree[mid].r-tree[mid].l+1);
    tree[mid+1].sum+=tree[v].inc*(tree[mid+1].r-tree[mid+1].l+1);
    tree[v].inc=0;
    return 0;
}

虽然我用了lazy 还是wa了两发,是因为当时理解的还不透彻。
在更新的时候,我的判断条件写的比较丑陋=-=
忽略了 更新完儿子们的sum值之后要把父亲也给更新了
虽然父亲把inc 沉降给了儿子,但儿子接下来还会再增加值,如果不加给父亲,那么父亲所存的sum就不正确了=-=
也就是一个pushup的过程
现在想想理所应当 感觉很自然,但当时就是制杖。。。

tree[v].sum=tree[v<<1].sum+tree[v<<1|1].sum;
//  Created by ZYD in 2015.
//  Copyright (c) 2015 ZYD. All rights reserved.
//

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define Size 100000
#define ll long long
#define mk make_pair
#define pb push_back
#define mem(array) memset(array,0,sizeof(array))
typedef pair<int,int> P;
struct date{
    ll l;
    ll r;
    long long sum,inc;
}tree[500000];
int build(ll L,ll R,int v){
    tree[v].l=L;
    tree[v].r=R;
    tree[v].inc=0;
    if(L==R){
        scanf("%lld",&tree[v].sum);
        return 0;
    }
    int mid=(L+R)>>1;
    build(L,mid,v<<1);
    build(mid+1,R,v<<1|1);
    // tree[v].w=tree[v<<1].w+tree[(v<<1)+1].w;
    tree[v].sum=tree[v<<1].sum+tree[v<<1|1].sum;
    return 0;
}
int pushdown(int mid,int v){

    if(tree[v].inc==0) return 0;    
    tree[mid].inc+=tree[v].inc;
    tree[mid+1].inc+=tree[v].inc;
    tree[mid].sum+=tree[v].inc*(tree[mid].r-tree[mid].l+1);
    tree[mid+1].sum+=tree[v].inc*(tree[mid+1].r-tree[mid+1].l+1);
    tree[v].inc=0;
    return 0;
}
int update(int v,ll x,ll y,long long c){
    if(tree[v].l==x && y==tree[v].r){
        tree[v].sum+=c*(y-x+1);
        tree[v].inc+=c;
        return 0;
    }
    if(tree[v].l==tree[v].r){
        return 0;
    }
    //如果上面没有return 则说明这里需要向子区间传递inc
    int mid=v<<1;
    int flag=0;
    pushdown(mid,v);//本身v区间增加的值 传递给儿子们
    if(y<=tree[mid].r){
        update(mid,x,y,c);
        flag=1;
    }

    if(tree[mid+1].l<=x){
        update(mid+1,x,y,c);
        flag=1;
    }
    if(flag==0){
        update(mid,x,tree[mid].r,c);
        update(mid+1,tree[mid+1].l,y,c);    
    }
    tree[v].sum=tree[v<<1].sum+tree[v<<1|1].sum;
    return 0;
}
long long ask(int v,int x,int y){
    if(tree[v].l==x && tree[v].r==y) return tree[v].sum;
    int mid=v<<1;
    pushdown(mid,v);
    if(tree[mid].r>=y){
        return ask(mid,x,y);
    }
    if(tree[mid+1].l<=x) return ask(mid+1,x,y);
    return (ask(mid,x,tree[mid].r)+ask(mid+1,tree[mid+1].l,y));
}
int main()
{
    //freopen("in.txt","r",stdin);
    int N,Q,x,y;
    long long a;
    char ch;
    scanf("%d%d",&N,&Q);
    build(1,N,1);
    /*for(int i=1;i<=N;i++){
        scanf("%lld",&a);
        update(1,i,i,a);
    }*/
    getchar();//cout<<tree[1].inc<<"*"<<endl;
    // cout<<tree[1].l<<tree[1].r<<tree[1].sum<<endl;
    for(int i=1;i<=Q;i++){
        scanf("%c %d %d",&ch,&x,&y);
        // cout<<ch;
        if(ch=='C'){
            scanf("%lld",&a);
            // cout<<x<<y<<"**";
            update(1,x,y,a);
        }
        if(ch=='Q'){
            // cout<<i<<"*"<<x<<y;
            printf("%lld\n",ask(1,x,y));
        }
        getchar();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值