北京大学OpenJudge 3243 A Simple Problem with Integers (线段树区间求和)

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37618760/article/details/82013937

描述

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.

输入

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.

输出

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

样例输入

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

样例输出

4
55
9
15

提示

The sums may exceed the range of 32-bit integers.

来源

POJ Monthly--2007.11.25, Yang Yi

Source  File

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int nCount=0;
int n,q,a,b,c;
struct CNode{
    int L,R;
    CNode *pLeft,*pRight;
    long long nSum;//原来的和
    long long lnc;
};
CNode Tree[200010];
void BuildTree(CNode *pRoot,int L,int R){
    pRoot->L=L;
    pRoot->R=R;
    pRoot->nSum=0;
    pRoot->lnc=0;
    if(L==R)
        return ;
    int mid=(L+R)/2;
    nCount++;
    pRoot->pLeft=Tree+nCount;
    nCount++;
    pRoot->pRight=Tree+nCount;
    BuildTree(pRoot->pLeft,L,mid);
    BuildTree(pRoot->pRight,mid+1,R);
}
void Insert(CNode *pRoot,int i,int v){
    pRoot->nSum+=v;
    if(pRoot->L==pRoot->R)
        return ;
    int mid=(pRoot->L+pRoot->R)/2;
    if(i<=mid)
        Insert(pRoot->pLeft,i,v);
    else
        Insert(pRoot->pRight,i,v);
}
void Add(CNode *pRoot,int a,int b,long long c){
    if(pRoot->L==a&&pRoot->R==b){
        pRoot->lnc+=c;
        return ;
    }
    pRoot->nSum+=c*(b-a+1);
    int mid=(pRoot->L+pRoot->R)/2;
    if(b<=mid)
        Add(pRoot->pLeft,a,b,c);
    else if(a>mid)
        Add(pRoot->pRight,a,b,c);
    else{
        Add(pRoot->pLeft,a,mid,c);
        Add(pRoot->pRight,mid+1,b,c);
    }
}
long long QuerynSum(CNode *pRoot,int a,int b){
    if(pRoot->L==a&&pRoot->R==b)
        return pRoot->nSum+(b-a+1)*pRoot->lnc;
    int mid=(pRoot->L+pRoot->R)/2;
    pRoot->nSum+=(pRoot->R-pRoot->L+1)*pRoot->lnc;
    Add(pRoot->pLeft,pRoot->L,mid,pRoot->lnc);
    Add(pRoot->pRight,mid+1,pRoot->R,pRoot->lnc);
    pRoot->lnc=0;

    if(b<=mid)
        return QuerynSum(pRoot->pLeft,a,b);
    else if(a>mid)
        return QuerynSum(pRoot->pRight,a,b);
    else{
        return QuerynSum(pRoot->pLeft,a,mid)+QuerynSum(pRoot->pRight,mid+1,b);
    }
}
int main(){
    cin>>n>>q;
    BuildTree(&Tree[0],1,n);
    for(int i=1;i<=n;i++){
        cin>>a;
        Insert(&Tree[0],i,a);
    }
    for(int i=0;i<q;i++){
        string cmd;
        cin>>cmd;
        if(cmd[0]=='C'){
            cin>>a>>b>>c;
            Add(Tree,a,b,c);
        }
        else{
            cin>>a>>b;
            cout<<QuerynSum(Tree,a,b)<<endl;
        }
    }
    return 0;
}

思路:这道题最大的问题在于总是Presentation Error,这是真的伤不起。

           简单说一下这道题的思路:

          给定Q (1 ≤ Q ≤ 100,000) 个数A 1 ,A 2 … A Q , ,
         以及可能多次进行的两个操作:
         1)  对某个区间A i  … A j 的 每个数 都加n(n 可变)
          2) 求某个区间A i  … A j 的数的和

          按照课件上的操作,我们可以这样做:

在增加时,如果要加的区间正好覆盖一个节点,则增加其节点的Inc 值,不再往下走,否则要更新nSum( 加上本次增量), 再将增
量往下传。这样更新的复杂度就是O(log(n))

这里,我们是执行C 1 7 2和 Q 2 6的过程。

         先说C 1 7 2

        1,7这个区间并不和1,9吻合,我们先记录nSum值为(7-1+1)*2=14

       再向下寻找,(1,5)这个区间吻合,我们就记录它们的增量为2

       再看(6,7)这个区间一开始和(6,9)并不吻合,我们记录nSum为(7-6+1)*2=4

      (6,7)在下一层吻合,记录增量Inc=2

        这个时候C操作结束,不做输出。

       而Q操作中,其实就是区间分解的过程。在查询时,如果待查区间不是正好覆盖一个节点,就将节点的Inc 往下带,然后将Inc代表的所有增量累加到nSum 上后将Inc 清0,接下来再往下查询。 一边查询,一边 Inc 往下带的过程也是区间分解的过程,复杂度也是O(log(n))

猜你喜欢

转载自blog.csdn.net/qq_37618760/article/details/82013937
今日推荐