POJ 3468 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 abc" means adding c to each of Aa, Aa+1, ... ,Ab. -10000 ≤ c ≤ 10000.
"Q ab" 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.



经典线段树区间更新,其实线段树的区间更新和点更新最大的区别在于增加了节点中增加add变量,由于点更新的话是在这个区间的全部节点都更新,但是如果每次的区间太大的时候是非常浪费时间的,所以区间更新的优越性在于他能通过add来暂时记录下当前点的全部子节点需要增加的数值,但是不在update中更新,而是在query查询用到该节点的时候才做更新,这样的话就能避免大区间点更新造成的时间开销,如此一来,每次都在区间更新的时候只是实际更新到要更新区间的通过二分产生的每个区间的最大子区间 的节点,其他的子节点在查询的时候或者是下一次更新到该节点子节点的时候继续向下通过之前记录的add更新。


具体代码思路:

///线段树区间更新
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<iomanip>
#include<string>
#include<climits>
#include<cmath>
#define INF 0x3f3f3f3f
#define MAX 110000
#define LL long long
using namespace std;
LL n,m;

struct Tree
{
  LL l,r;
  LL sum,add;
};
Tree tree[MAX*3];

void pushup(LL x)  ///更新父节点
{
  tree[x].sum=tree[ x<<1 ].sum+tree[ (x<<1)+1 ].sum;
}

void pushdown(LL x)  ///用于更新add数组
{
  LL tmp = x<<1 ;
  tree[tmp].add +=  tree[x].add;  ///由子节点通过增加
  tree[tmp+1].add += tree[x].add;
  tree[tmp].sum += tree[x].add*(tree[tmp].r-tree[tmp].l+1);
  tree[tmp+1].sum += tree[x].add*(tree[tmp+1].r-tree[tmp+1].l+1);
  tree[x].add=0;
}

void build(int l,int r,int x)
{
  tree[x].l=l , tree[x].r=r , tree[x].add=0;
  if(l==r)
  {
    scanf("%lld",&tree[x].sum);  ///子节点初始化
    return ;
  }
  int tmp=x<<1;
  int mid=(l+r)>>1;
  build(l,mid,tmp);
  build(mid+1,r,tmp+1);
  pushup(x);  ///建立时需根据子节点更细父亲节点
}


void update(LL l,LL r,LL c,LL x)  ///分别表示区间的左 , 右 , 增加的值  ,当前父亲节点
{
  if(r<tree[x].l||l>tree[x].r)   return ;
  if(l<=tree[x].l&&r>=tree[x].r)  ///该区间为需要更新区间的子区间
  {
    tree[x].add += c;
    tree[x].sum += c*(tree[x].r-tree[x].l+1); ///区间长度*要增加的数值
    return ;
  }
  
  ///如果比当前的范围还小,就通过该节点向下更新下面的节点
  if(tree[x].add) pushdown(x);  ///更新从上向下更新add
  LL tmp=x<<1;
  update(l,r,c,tmp);
  update(l,r,c,tmp+1);
  pushup(x);
}

LL query(LL l,LL r,LL x)
{
  ///if(r<tree[x].l||l>tree[x].r)	    return -INF;//要更新的区间不在该区间上(输入有误)
  if(l<=tree[x].l&&r>=tree[x].r)	  ///要计算的区间包括了该区间
  {
    return tree[x].sum;
  }
  if(tree[x].add)   pushdown(x);  ///如果add不为零,对查询可能用到的add更新
  LL tmp=x<<1;
  LL mid=(tree[x].l+tree[x].r)>>1;

  if(r<=mid)   return query(l,r,tmp);
  else if(l>mid)  return query(l,r,tmp+1);
  else  return query(l,mid,tmp)+query(mid+1,r,tmp+1);
}

int main()
{
  while(~scanf("%lld %lld",&n,&m))
  {
    build(1,n,1);
    char str[5];
    while(m--)
    {
      scanf("%s",str);
      if(str[0]=='Q')
      {
        LL l,r;
        scanf("%lld %lld",&l,&r);
        printf("%lld\n",query(l,r,1));
      }
      else
      {
        LL l,r,c;
        scanf("%lld %lld %lld",&l,&r,&c);
        update(l,r,c,1);
      }
    }
  }
  return 0;
}


因为在更新时候和Build不一样,所以代码风格很差,所以做一个整洁的。。。

///线段树区间更新
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<iomanip>
#include<string>
#include<climits>
#include<cmath>
#define INF 0x3f3f3f3f
#define MAX 110000
#define LL long long
using namespace std;
LL n,m;

struct Tree
{
  LL l,r;
  LL sum,add;
};
Tree tree[MAX*3];

void pushup(LL x)  ///更新父节点
{
  tree[x].sum=tree[ x<<1 ].sum+tree[ (x<<1)+1 ].sum;
}

void pushdown(LL x)  ///用于更新add数组
{
  LL tmp = x<<1 ;
  tree[tmp].add +=  tree[x].add;  ///由子节点通过增加
  tree[tmp+1].add += tree[x].add;
  tree[tmp].sum += tree[x].add*(tree[tmp].r-tree[tmp].l+1);
  tree[tmp+1].sum += tree[x].add*(tree[tmp+1].r-tree[tmp+1].l+1);
  tree[x].add=0;
}

void build(int l,int r,int x)
{
  tree[x].l=l , tree[x].r=r , tree[x].add=0;
  if(l==r)
  {
    scanf("%lld",&tree[x].sum);  ///子节点初始化
    return ;
  }
  int tmp=x<<1;
  int mid=(l+r)>>1;
  build(l,mid,tmp);
  build(mid+1,r,tmp+1);
  pushup(x);  ///建立时需根据子节点更细父亲节点
}


void update(LL l,LL r,LL c,LL x)  ///分别表示区间的左 , 右 , 增加的值  ,当前父亲节点
{
  if(r<tree[x].l||l>tree[x].r)   return ;
  if(l<=tree[x].l&&r>=tree[x].r)  ///该区间为需要更新区间的子区间
  {
    tree[x].add += c;
    tree[x].sum += c*(tree[x].r-tree[x].l+1); ///区间长度*要增加的数值
    return ;
  }

  ///如果比当前的范围还小,就通过该节点向下更新下面的节点
  if(tree[x].add) pushdown(x);  ///更新从上向下更新add

  LL tmp=x<<1;
  LL mid=(tree[x].l+tree[x].r)>>1;
  if(r<=mid)    update(l,r,c,tmp);
  else if(l>mid)   update(l,r,c,tmp+1);
  else{
        update(l,mid,c,tmp) ;
        update(mid+1,r,c,tmp+1);
  }

  pushup(x);
}

LL query(LL l,LL r,LL x)
{
  ///if(r<tree[x].l||l>tree[x].r)	    return -INF;//要更新的区间不在该区间上(输入有误)
  if(l<=tree[x].l&&r>=tree[x].r)	  ///要计算的区间包括了该区间
  {
    return tree[x].sum;
  }
  if(tree[x].add)   pushdown(x);  ///如果add不为零,对查询可能用到的add更新
  LL tmp=x<<1;
  LL mid=(tree[x].l+tree[x].r)>>1;

  if(r<=mid)   return query(l,r,tmp);
  else if(l>mid)  return query(l,r,tmp+1);
  else  return query(l,mid,tmp)+query(mid+1,r,tmp+1);
}

int main()
{
  while(~scanf("%lld %lld",&n,&m))
  {
    build(1,n,1);
    char str[5];
    while(m--)
    {
      scanf("%s",str);
      if(str[0]=='Q')
      {
        LL l,r;
        scanf("%lld %lld",&l,&r);
        printf("%lld\n",query(l,r,1));
      }
      else
      {
        LL l,r,c;
        scanf("%lld %lld %lld",&l,&r,&c);
        update(l,r,c,1);
      }
    }
  }
  return 0;
}


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