POJ 3468-A Simple Problem with Integers(线段树)

A Simple Problem with Integers

Description

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


此题除了用到了线段树的基本性质(求和)以外,还用到了区间修改。主要是加入了延迟标记缩短对区间修改的时间,十分便利。本题也是懂了如何利用此工具以后就变成一道不难的题(但是我连工具也不会用啊……)

为了加强理解,强迫自己写了注释,思路就不写了,都放在注释里。

用G++交会超时,C++的话连上限5s的一半时间都没达到……

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3

struct Node{
    long long sum,val;
}node[400005];

void pushup(int tr){
    node[tr].sum=node[tr*2].sum+node[tr*2+1].sum;
}

void pushdown(int tr,int m){
    if (node[tr].val) {
        node[tr*2].val+=node[tr].val;
        node[tr*2+1].val+=node[tr].val;
        node[tr*2].sum+=(long long)(m-m/2)*node[tr].val;
        node[tr*2+1].sum+=(long long)m/2*node[tr].val;
        node[tr].val=0;
    }
}

void build(int l,int r,int tr){//递归构造线段树,默认延迟标记为0
    node[tr].val=0;
    if (l==r) {
        scanf("%lld",&node[tr].sum);
        return ;
    }
    int m=(l+r)/2;
    build(l, m, tr*2);
    build(m+1, r, tr*2+1);
    pushup(tr);//区间求和,每个节点表示的就是该区域内之和。
}

long long query(int L,int R,int l,int r,int tr){//L,R分别需要查询的区间的左右界限。
    if (L<=l&&r<=R) {
        return node[tr].sum;//此时查询区间大于该节点区间,因此加上该节点区间和
    }
    int m=(l+r)/2;
    pushdown(tr, r-l+1);//将延迟标记向下传递,并将相应的和的变化反应到子节点上。之后对子节点区间求和时即可将延迟标记代表的和算上。
    long long ans=0;
    //已知此时l与r必然存在l<L或r>R,即l,r此时不被查询区间包括
    if (L<=m) {
        //查询区间的左边界小于m时,将查询区间放到l到m区间内进行求和。
        ans+=query(L,R,l,m,tr*2);
    }
    if (m<R) {
        //同上,通过这两步,即可完成求和工作。
        ans+=query(L, R, m+1, r, tr*2+1);
        pushup(tr);
    }
    return ans;
}
void update(int L,int R,int add,int l,int r,int tr){//区间修改的重头戏,add为修改的大小
    if(L<=l&&r<=R)//修改区间大于该节点代表的区间时,算上add所改变的和的大小,并相应修改延迟标记。
    {
        node[tr].sum+=(long long)add*(r-l+1);
        node[tr].val+=add;
        return ;
    }
    pushdown(tr,r-l+1);//同上,将延迟标记传递。
    int m=(l+r)/2;
    if(L<=m)update(L,R,add,l,m,tr*2);//递归修改,同上,不赘述了,与query函数内涵相同。
    if(m<R)update(L,R,add,m+1,r,tr*2+1);
    pushup(tr);
}
int main()
{
    int n,q,a,b;
    int c;
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        build(1,n,1);//构造子树
        char s[3];
        while(q--)
        {
            scanf("%s",s);
            if(s[0]=='Q')
            {
                scanf("%d%d",&a,&b);
                printf("%lld\n",query(a, b, 1, n, 1));
            }
            else if(s[0]=='C')
            {
                scanf("%d%d%d",&a,&b,&c);
                update(a,b,c,1,n,1);
            }
        }
    }
    return 0;
}


你可能感兴趣的:(POJ 3468-A Simple Problem with Integers(线段树))