标签:
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
给你一个数列,每次询问一个区间的和,或者每次将一个区间的所有元素都加上一个数
树状数组天生用来动态维护数组前缀和,其特点是每次更新一个元素的值,查询只能查数组的前缀和,
但这个题目求的是某一区间的数组和,而且要支持批量更新某一区间内元素的值,怎么办呢?实际上,
还是可以把问题转化为求数组的前缀和。
首先,看更新操作$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 }
POJ 3468 A Simple Problem with Integers
标签:
原文地址:http://www.cnblogs.com/crackpotisback/p/4784119.html