poj3468 树状数组解法(树状数组维护区间更新)

来源:互联网 发布:琪琪影院软件下载 编辑:程序博客网 时间:2024/05/16 16:07

这个题目是一个经典的线段树维护区间跟新问题。后来在看书装数组维护区间和的时候,发现有好多大神说这个题目也可以用树状数组来解。后来就也研究了一下。当然,和大多数用树状数组解的题目的思路差不多,只是这个不太好想。根据树状数组的特点:树状数组是维护前缀和的经典做法,它的更新是对数组中的一个元素的,就是每次更新只能改变一个元素的值。那么,如何用树状数组来维护区间更新呢。源数据放在a[]数组中。这就需要构造数组(这也是树状数组题目的大多做法,也是树状数组解题 的一个难点)。如何构造数组:构造数组c[],d[];其中:c[i]表示区间[i,n]的增量,那么,更新(x,y,z)就比较简单为update_c(x,z),update_c(y+1,-z)(即:c[x]+z,c[y+1]-z)。求和:s[x] = a[1]+a[2]+...+a[x]+c[1]*x+c[2]*(x-1)+...c[x]*(x+1-i) = {a[1]+a[2]+...+a[x]} + {(x+1)*(c[1]+c[2]+...+c[x])} - {1*c[1]+2*c[2]+...+x*c[x]};那么,我们令d[i] = c[i]*i;则d[]的更新为:update_d(x,x*c[x]),update_d(y+1,(y+1)*c[y+1]);

构造依据:树状数组的特点,求和:求的是前缀和;更新:一次只能改变一个元素的值。

这样就可以用树状数组来解决区间更新的问题了;

poj3468

//树状数组维护区间更新求和 #include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define MAX 100010#define ll long longusing namespace std;ll c[MAX],d[MAX],a[MAX];int n,q;int lowbit(int x){return x&(-x);}void update1(int x, ll u){      //更新c数组for (int i = x; i<=n; i+=lowbit(i)){c[i] += u;}}void update2(int x, ll u){     //更新d数组for (int i = x; i<=n; i+=lowbit(i)){d[i] += u;}}ll sum1(int x){<span style="white-space:pre"></span>//c数组求前缀和ll s = 0;for (int i = x; i>0; i-=lowbit(i)){s += c[i];}return s;}ll sum2(int x){<span style="white-space:pre"></span>//d数组求前缀和ll s = 0;for (int i = x; i>0; i-=lowbit(i)){s += d[i];}return s;}int main(){while (scanf("%d%d",&n,&q) != EOF){memset(a,0,sizeof(a));memset(c,0,sizeof(c));memset(d,0,sizeof(d));ll t;for (int i = 1; i<=n; i++){scanf("%lld",&t);a[i] = a[i-1] + t;}char s[5];while (q--){scanf("%s",s);int  x,y;ll z;if (s[0] == 'Q'){scanf("%d%d",&x,&y);ll t1 = a[y] - a[x-1];ll t2 = (y+1)*sum1(y) - x*sum1(x-1);ll t3 = sum2(y) - sum2(x-1);printf("%lld\n",t1+t2-t3);}else{scanf("%d%d%lld",&x,&y,&z);    //注意理解如何更新update1(x,z);update1(y+1,-z);update2(x,x*z);update2(y+1,-(y+1)*z);}}}return 0;}


0 0
原创粉丝点击