码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 3468 A Simple Problem with Integers

时间:2015-09-05 23:33:38      阅读:408      评论:0      收藏:0      [点我收藏+]

标签:

A Simple Problem with Integers

Time Limit: 5000ms
Memory Limit: 131072KB
This problem will be judged on PKU. Original ID: 3468
64-bit integer IO format: %lld      Java class name: Main
 

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 AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000."Q a b" means querying the sum of AaAa+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

Source

 
解题:学习下树状数组的改段求段。。。确实很奇妙
 

给你一个数列,每次询问一个区间的和,或者每次将一个区间的所有元素都加上一个数

 

    树状数组天生用来动态维护数组前缀和,其特点是每次更新一个元素的值,查询只能查数组的前缀和,

但这个题目求的是某一区间的数组和,而且要支持批量更新某一区间内元素的值,怎么办呢?实际上,

还是可以把问题转化为求数组的前缀和。

 

    首先,看更新操作$update(s, t, d)$把区间$A[s]...A[t]$都增加$d$,我们引入一个数组$delta[i]$,表示

$A[i]...A[n]$的共同增量,n是数组的大小。那么update操作可以转化为:

1)令$delta[s] = delta[s] + d$,表示将$A[s]...A[n]$同时增加$d$,但这样$A[t+1]...A[n]$就多加了$d$,所以

2)再令$delta[t+1] = delta[t+1] - d$,表示将$A[t+1]...A[n]$同时减$d$

 

    然后来看查询操作$query(s, t)$,求$A[s]...A[t]$的区间和,转化为求前缀和,设$sum[i] = A[1]+...+A[i]$,则

                            $A[s]+...+A[t] = sum[t] - sum[s-1]$,

那么前缀和$sum[x]$又如何求呢?它由两部分组成,一是数组的原始和,二是该区间内的累计增量和, 把数组A的原始

值保存在数组org中,并且$delta[i]$对$sum[x]$的贡献值为$delta[i]\times (x+1-i)$,那么

                            \[ sum[x] = org[1]+...+org[x] + delta[1]\times x + delta[2]\times (x-1) + delta[3]\times (x-2)+...+delta[x]\times 1\]

                                         \[= org[1]+...+org[x] + \sum_{i = 1}^{x}(delta[i]\times (x+1-i))\]

                                        \[ sum[x] = \sum_{i = 1}^{x}(org[i]) + (x+1)\times\sum_{i = 1}^{x}(delta[i]) - \sum_{i = 1}^{x}(delta[i]\times i) \]

可以转化为 $\sum_{i=1}^{x}(org[i]-delta[i]*i)+(x+1)*delta[i]$

这其实就是三个数组org[i], delta[i]和delta[i]*i的前缀和,org[i]的前缀和保持不变,事先就可以求出来,delta[i]和

delta[i]*i的前缀和是不断变化的,可以用两个树状数组来维护。

技术分享
 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 typedef long long LL;
 6 const int maxn = 100010;
 7 LL s[maxn],c[2][maxn];
 8 int n,m;
 9 void add(LL *c,int i,LL val) {
10     while(i <= n) {
11         c[i] += val;
12         i += i&-i;
13     }
14 }
15 LL sum(LL *c,int i,LL ret = 0) {
16     while(i > 0) {
17         ret += c[i];
18         i -= i&-i;
19     }
20     return ret;
21 }
22 int main() {
23     char cmd[4];
24     LL x,y,z;
25     while(~scanf("%d%d",&n,&m)) {
26         memset(c,0,sizeof c);
27         for(int i = 1; i <= n; ++i) {
28             scanf("%lld",s + i);
29             s[i] += s[i-1];
30         }
31         while(m--) {
32             scanf("%s",cmd);
33             if(cmd[0] == C) {
34                 scanf("%lld%lld%lld",&x,&y,&z);
35                 add(c[0],x,z);
36                 add(c[0],y+1,-z);
37                 add(c[1],x,x*z);
38                 add(c[1],y+1,-z*(y+1));
39             } else {
40                 scanf("%lld%lld",&x,&y);
41                 printf("%lld\n",s[y] - s[x-1] + (y+1)*sum(c[0],y) - sum(c[1],y) - x*sum(c[0],x-1) + sum(c[1],x-1));
42             }
43         }
44     }
45     return 0;
46 }
View Code

 

POJ 3468 A Simple Problem with Integers

标签:

原文地址:http://www.cnblogs.com/crackpotisback/p/4784119.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!