poj 3468 树状数组解法

转自
https://blog.csdn.net/non_cease/article/details/7435052?tdsourcetag=s_pcqq_aiomsg

树状数组F:
一 . 首先,看更新操作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

此番操作后,对delta[i]求前缀和,就是单点查询。(此番操作是为了后边求delta[i]对sum[x]的贡献)

二 .解题思路从暴力出发,求区间和,就是求 右端点t的前缀和sum[t]-左端点s-1的前缀和sum[s-1]。
前缀和如何求?
分为俩部分
1、原数组的和。将其存在一个数组A[]中。
2、该区间内累计增量的和。delta[i]对sum[x]的贡献为(x+1-i)delta[i] (自己带俩个数试试,确实如此)。
综上1、2:
sum[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))
  
  =segmaA[i]+(x+1)*segma(delta[i])-segma(delta[i]*i)   ,1<=i<=x。 (该化简的公式下面会用到)

segma就是求前缀和,因此创建三个树状数组,一个是原数组,一个是delta[i],一个是delta[i]*i。

下面就是代码了:

#include<iostream>
#include<string>
using namespace std;
long long a1[100002];//delta[i];
long long a2[100002];//delta[i]*i;
long long tree[100002];
long long a[100002];
int N;
long long lowbit(long long x)
{
	return x & -x;
}
void updata(long long *an,long long x, long long k) //单点更新函数
{
	for (long long i = x; i <= N; i += lowbit(i)) {
		an[i] += k;
	}
}
long long getsum(long long *an,long long x) //求前缀和函数
{
	long long ans = 0;
	for (long long i = x; i > 0; i -= lowbit(i)) {
		ans += an[i];
	}
	return ans;
}
int main()
{
	int Q;
	long long x, y;		//区间左右端点
	cin >> N >> Q;
	for (int i = 1; i <= N; i++) {
		cin >> a[i];		//原数组
		updata(tree,i, a[i]);		//原数组更新到树状数组
	}
	while(Q--){
		char q;
		cin >> q;
		cin >> x >> y;
		if(q=='Q'){
			long long sum = 0;			//由上面公式得如下操作
			sum += getsum(tree, y) + (y + 1) * getsum(a1, y) - getsum(a2, y);	
			sum -= getsum(tree, x - 1) + (x - 1 + 1) * getsum(a1, x - 1) - getsum(a2, x - 1);
			cout << sum << endl;
		}
		else if (q == 'C') {
			int w;
			cin >> w;
			updata(a1, x,w );		//俩个为了求端点sum[x]所开的树状数组的更新
			updata(a1, y + 1, -w);
			updata(a2, x, x * w);
			updata(a2, y + 1, -w * (y + 1));
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值