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

这个题目是一个经典的线段树维护区间跟新问题。后来在看树状数组维护区间和的时候,发现有好多大神说这个题目也可以用树状数组来解。后来就也研究了一下。当然,和大多数用树状数组解的题目的思路差不多,只是这个不太好想。根据树状数组的特点:树状数组是维护前缀和的经典做法,它的更新是对数组中的一个元素的,就是每次更新只能改变一个元素的值。那么,如何用树状数组来维护区间更新呢。源数据放在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 long

using 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){   //c数组求前缀和
	ll s = 0;
	for (int i = x; i>0; i-=lowbit(i)){
		s += c[i];
	}
	return s;
}

ll sum2(int x){   //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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值