树状数组~序列前缀和的维护

树状数组c[x]存储某一数组b的元素在区间[x-lowbit(x),x]的统计.

前缀和维护原理

对 于 正 整 数 x , 其 二 进 制 表 示 可 写 为 ∑ i = 1 n 2 a [ i ] 的 形 式 , 若 数 组 a 递 减 排 序 , 对于正整数x,其二进制表示可写为\sum_{i=1}^{n} 2^{a[i]} 的形式,若数组a递减排序, x,i=1n2a[i],a,
则 有 x − l o w b i t ( x ) = ∑ i = 1 n − 1 2 a [ i ] , 此 时 划 定 区 间 [ ∑ i = 1 n − 1 2 a [ i ] + 1 , ∑ i = 1 n 2 a [ i ] ] , 则有x-lowbit(x) = \sum_{i=1}^{n-1} 2^{a[i]} ,此时划定区间[\sum_{i=1}^{n-1} 2^{a[i]}+1,\sum_{i=1}^{n} 2^{a[i]} ], xlowbit(x)=i=1n12a[i],[i=1n12a[i]+1,i=1n2a[i]],
此 时 令 x = x − l o w b i t ( x ) , 重 复 上 述 操 作 直 至 x = 0 , 则 此 时 有 若 干 区 间 : 此时令x=x-lowbit(x),重复上述操作直至x = 0,则此时有若干区间: x=xlowbit(x),x=0,:
[ 1 , ∑ i = 1 1 2 a [ i ] ] , [ ∑ i = 1 1 2 a [ i ] + 1 , ∑ i = 1 2 2 a [ i ] ] , . . . . . . , [ ∑ i = 1 n − 2 2 a [ i ] + 1 , ∑ i = 1 n − 1 2 a [ i ] ] , [ ∑ i = 1 n − 1 2 a [ i ] + 1 , ∑ i = 1 n 2 a [ i ] ] , [1,\sum_{i=1}^{1}2^{a[i]}],[\sum_{i=1}^{1}2^{a[i]}+1,\sum_{i=1}^{2}2^{a[i]}],......,[\sum_{i=1}^{n-2}2^{a[i]}+1,\sum_{i=1}^{n-1}2^{a[i]}],[\sum_{i=1}^{n-1}2^{a[i]}+1,\sum_{i=1}^{n}2^{a[i]}], [1,i=112a[i]],[i=112a[i]+1,i=122a[i]],......,[i=1n22a[i]+1,i=1n12a[i]],[i=1n12a[i]+1,i=1n2a[i]],
我 们 以 c [ x ] 代 表 数 组 b 在 区 间 [ x − l o w b i t ( x ) + 1 , x ] 元 素 的 值 的 总 和 , 我们以c[x]代表数组b在区间[x-lowbit(x)+1,x]元素的值的总和, c[x]b[xlowbit(x)+1,x],
即 c [ x ] = ∑ i = x − l o w b i t ( x ) + 1 x b [ i ] , 则 由 上 述 有 数 组 b 的 前 缀 和 公 式 : 即c[x]=\sum_{i=x-lowbit(x)+1}^{x}b[i],则由上述有数组b的前缀和公式: c[x]=i=xlowbit(x)+1xb[i],b:
s u m [ x ] = ∑ i = 1 ∑ j = 1 1 2 a [ j ] b [ i ] + ∑ i = ( ∑ j = 1 1 2 a [ j ] + 1 ) ∑ j = 1 2 2 a [ j ] b [ i ] + . . . + ∑ i = ( ∑ j = 1 n − 1 2 a [ j ] + 1 ) ∑ j = 1 n 2 a [ j ] b [ i ] = c [ 2 a [ 1 ] ] + c [ 2 a [ 1 ] + 2 a [ 2 ] ] + . . . + c [ x − l o w b i t ( x ) ] + c [ x ] ; sum[x] = \sum_{i=1}^{\sum_{j=1}^{1}2^{a[j]}}b[i]+\sum_{i=(\sum_{j=1}^{1}2^{a[j]}+1)}^{\sum_{j=1}^{2}2^{a[j]}}b[i]+...+\sum_{i=(\sum_{j=1}^{n-1}2^{a[j]}+1)}^{\sum_{j=1}^{n}2^{a[j]}}b[i] = c[2^{a[1]}]+c[2^{a[1]}+2^{a[2]}]+...+c[x-lowbit(x)]+c[x]; sum[x]=i=1j=112a[j]b[i]+i=(j=112a[j]+1)j=122a[j]b[i]+...+i=(j=1n12a[j]+1)j=1n2a[j]b[i]=c[2a[1]]+c[2a[1]+2a[2]]+...+c[xlowbit(x)]+c[x];
由 上 述 我 们 得 到 了 利 用 树 状 数 组 c 求 数 组 b 前 缀 和 的 一 种 方 法 , 具 体 代 码 实 现 如 下 : 由上述我们得到了利用树状数组c求数组b前缀和的一种方法,具体代码实现如下: cb,:

ll ans = 0;
for(;x;x-=lowbit(x)) 
	ans += c[x];

性质

对于数组c的树状结构,该结构满足以下性质:

  1. 每个节点c[x]保存以它为根的子树中所有叶节点的和。
  2. 每个节点c[x]的子节点个数等于lowbit(x)的位数。
  3. 除了树根外,每个节点c[x]的父节点为c[x+lowbit[x]]。
  4. 树的深度为O(logN)。

简单理解:
对于x,c[x]对应区间为[x-lowbit(x)+1,x],我们寻找一个最小的数y(最近),使得c[y]对应区间包含且大于区间[x-lowbit(x)+1,x],则有:
{ y − l o w b i t ( y ) < = x − l o w b i t ( x ) y > x ( 此 处 因 为 包 含 关 系 故 y ≠ x ) ⇒ x < y < = x + l o w b i t ( y ) − l o w b i t ( x ) \begin{cases} & y-lowbit(y)<=x-lowbit(x) \\ & y >x(此处因为包含关系故y\ne x)\\ \end{cases} \Rightarrow x<y<=x+lowbit(y)-lowbit(x) {ylowbit(y)<=xlowbit(x)y>x(y=x)x<y<=x+lowbit(y)lowbit(x)
由于lowbit(y)和lowbit(x)二进制下含有1的位都只有一个一位,则可得lowbit(y)比lowbit(x)大一位,即lowbit(y) = 2*lowbit(x),由此即可推得性质3,若此进一步推进还可得到另外几条性质.

关于lowbit运算

lowbit(x)表示x二进制表示下最低位的1表示的值,当我们将x的二进制所有位取反时,原先lowbit(x)位置变成0,其后面的数变成1,此时我们将x加1,则最开始lowbit(x)的位置变成1,其后面的位置都为0,前面的位置各位的值不同,此时进行且运算,得到的值即为lowbit(x)的值,由于我们计算机的工作原理,以上过程可写为lowbit(x)=x&-x.

单点操作

给序列中的元素a[x]加上一个数d,同时维护数组a的前缀和,此时由此节点c[x]开始对其祖先进行更新,由于树的最大深度为logN,故时间复杂度为O(logN),具体代码实现如下:

#define lowbit(x) (x&-x)
void add(int x,int d)
{
	for(;x<=N,x+=lowbit(x))
		c[x] += d;
} 

初始化

  1. 建立一个全为0的数组c,对每个x执行add(x,a[x])操作. 时间复杂度O(NlogN).
  2. 从小到大扫描每个节点x,借助lowbit运算扫描子节点并求和. 时间复杂度O(N).

例题与应用

题目来源:POJ - 2352
题目大意:给你星星的坐标(y递增,若y相等,x递增),每个星星都有一个等级,规定它的等级就是在它左下方的星星的个数。输入所有星星后,依次输出等级为0到n-1的星星的个数。
解题思路:由于y递增,我们可以由1~n正序扫描输入的数组,然后用树状数组c[i]代表x坐标在[x-lowbit+1,x]之间的星星的出现次数,依次更新c[x],当第i次更新时,第i颗星星的level即为前缀和sum[x[i]],由于树状数组下标不能小于等于0,我们对x坐标加一,具体代码实现如下.

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 32005;
int a[maxn],x,y,n,ans[maxn];
int main()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;++i)
	{
		scanf("%d%d",&x,&y);
		int res = 0;
		for(int j = x+1;j;j-=(j&(-j)))
			res += a[j];
		ans[res]++;
		for(int j = x+1;j <= maxn;j+=(j&(-j)))
			a[j]++;
	}
	for(int i = 0;i < n;++i) 
		printf("%d\n",ans[i]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值