ACM: 线段树 poj 3468

本文介绍了一种使用线段树解决区间更新与查询问题的经典算法。通过维护每个节点的累加和与增量域,可以高效地处理区间内数值的批量增加及区间和的查询。文章详细解释了解题思路,并提供了完整的C++实现代码。
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

Hint

The sums may exceed the range of 32-bit integers.
 
题意: 一组数组, 对它有两种操作, Q A B: 访问区间[A,B]的数组累加和, C A B C: 区间[A,B]内每个
      元素都加上C.
 
解题思路:
    1. 经典的线段树题目, 只需在线段树每个节点上设置sum和add: 累加和,增量域. 题目迎刃而解.
    2. 操作C时, 当满足当前区间[A,B]时, p->add = p->add + C; 修改区间的增量即可.
                 当不满足时, 继续往左右区间改变增量之前要将当前的区间增量加入sum,
                             p->sum = p->sum + (p->r - p->l + 1) * p->add;
       操作Q时,当满足当前区间[A,B]时, 记录结果result = p->sum + (p->r - p->l + 1)*p->add;
               当不满足时, 继续往左右区间查找之前要将当前的增量分配到左右子树区间上,
               p->left->add += p->add; p->right->add += p->add; p->add = 0; //最后记得清除.
               p->sum += (p->r - p->l + 1)*p->add;
     3. 最后, 要注意累加的结果会超出32位整型, 并且传入C增量时要用整型64位.
 
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 100005
struct node
{
 int l, r;
 __int64 sum, add; //sum:区间和, add:区间的增量
}pt[MAX*4];
int n, m;
__int64 g[MAX];
__int64 a, b, c;
char ch[2];
__int64 result;
void buildTree(int l, int r, int pos)
{
 pt[pos].l = l, pt[pos].r = r;
 pt[pos].add = 0;
 if(l == r)
 {
  pt[pos].sum = g[l];
  return ;
 }
 int mid = (pt[pos].l+pt[pos].r)/2;
 buildTree(l, mid, pos*2);
 buildTree(mid+1, r, pos*2+1);
 pt[pos].sum = pt[pos*2].sum+pt[pos*2+1].sum;
}
void add(int l, int r, int pos, __int64 c)
{
 if(pt[pos].l == l && pt[pos].r == r)
 {
  pt[pos].add += c;
  return ;
 }
 int mid = (pt[pos].l+pt[pos].r)/2;
 pt[pos].sum += (r-l+1)*c;
 if(r <= mid)
  add(l, r, pos*2, c);
 else if(l > mid)
  add(l, r, pos*2+1, c);
 else
 {
  add(l, mid, pos*2, c);
  add(mid+1, r, pos*2+1, c);
 }
}
void find(int l, int r, int pos)
{
 if(pt[pos].l == l && pt[pos].r == r)
 {
  result += (pt[pos].sum + (r-l+1)*pt[pos].add);
  return ;
 }
 int mid = (pt[pos].l+pt[pos].r)/2;
 pt[pos].sum += (pt[pos].r-pt[pos].l+1)*pt[pos].add;
 pt[pos*2].add += pt[pos].add;
 pt[pos*2+1].add += pt[pos].add;
 pt[pos].add = 0;
 if(r <= mid)
  find(l, r, pos*2);
 else if(l > mid)
  find(l, r, pos*2+1);
 else
 {
  find(l, mid, pos*2);
  find(mid+1, r, pos*2+1);
 }
}
int main()
{
 int i;
// freopen("input.txt","r",stdin);
 while(scanf("%d %d",&n, &m) != EOF)
 {
  for(i = 1; i <= n; ++i)
   scanf("%I64d",&g[i]);
  buildTree(1, n, 1);
  for(i = 1; i <= m; ++i)
  {
   scanf("%s",ch);
   if(ch[0] == 'C')
   {
    scanf("%I64d %I64d %I64d",&a, &b, &c);
    add(a, b, 1, c);
   }
   else if(ch[0] == 'Q')
   {
    scanf("%d %d",&a, &b);
    result = 0;
    find(a, b, 1);
    printf("%I64d\n",result);
   }
  }
 }
 return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值