poj2346_A Simple Problem with Integers

来源:互联网 发布:手办 淘宝 推荐 编辑:程序博客网 时间:2024/06/05 06:02

题目:http://poj.org/problem?id=3468

算法来自:http://blog.csdn.net/non_cease/article/details/7435052

树状数组天生用来动态维护数组前缀和,其特点是每次更新一个元素的值,查询只能查数组的前缀和。但这个题目求的是某一区间的数组和,而且要支持批量更新某一区间内元素的值。所以这道题的关键就是成段更新树状数组的问题。

思路就是把问题转化为求数组的前缀和。

 

       首先,看更新操作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

 

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

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

那么前缀和sum[x]又如何求呢?它由两部分组成,一是数组的原始和,二是该区间内的累计增量和, 把数组A的原始和保存在数组sum中,并且delta[i]对sum(1...x)的贡献值为delta[i]*(x+1-i),那么

                            sum(1...x) = A[1]+...+A[x] + delta[1]*x + delta[2]*(x-1) + delta[3]*(x-2)+...+delta[x]*1

                                         = A[1]+...+A[x] + segma(delta[i]*(x+1-i))

                                         = segma(A[i]) + (x+1)*segma(delta[i]) - segma(delta[i]*i),1 <= i <= x

                                         = sum[i] + (x+1)*segma(delta[i]) - segma(delta[i]*i),1 <= i <= x

这其实就是三个数组[i], delta[i]和delta[i]*i的前缀和,A[i]的前缀和保持不变,事先就可以求出来存在sum[]中,delta[i]和delta[i]*i的前缀和是不断变化的,可以用两个树状数组来维护,分别设为delta1和delta2。


代码如下:

注意易错的地方"%I64d",无论是输出还是录入时候

#include<iostream>using namespace std;#define maxn 100002int array[maxn];__int64 ori[maxn],delta1[maxn],delta2[maxn];int n;int lowbit(int t){return t&(-t);}void add(__int64 tree[],int i,int num){while(i <= n){tree[i] += num;i += lowbit(i);}}__int64 getSum(__int64 tree[],int i){__int64 ret = 0;while(i > 0){ret += tree[i];i -= lowbit(i);}return ret;}int main(){int q,a,b,c;char command;__int64 sum;scanf("%d %d",&n,&q);memset(ori,0,sizeof(ori));memset(delta1,0,sizeof(delta1));memset(delta2,0,sizeof(delta2));for(int i = 1;i<=n;i++){scanf("%I64d",&ori[i]);ori[i] += ori[i-1]; }while(q--){getchar();scanf("%c %d %d",&command,&a,&b);if(command == 'Q'){sum = ori[b] - ori[a-1];sum += ((b+1)*getSum(delta1,b) - a*getSum(delta1,a-1));sum += (getSum(delta2,a-1) - getSum(delta2,b) );//here is outputprintf("%I64d\n",sum);}else if(command == 'C'){scanf("%d",&c);add(delta1,a,c);add(delta1,b+1,-c);add(delta2,a,a*c);add(delta2,b+1,(b+1)*(-c));}}return 0;}
1032MS

原创粉丝点击