树状数组应用

一维树状数组常用的3个函数

int lowbit(int x) //取x的最低位1,比如4,则返回4,如5,则返回1
{
	return x&(-x);
}

void update(int i, int val)  //将第i个元素增加val
{
	//i的祖先都要增加val
	while(i <= n)
	{
		sum[i] += val;
		i += lowbit(i);   //将i的二进制未位补为得到其祖先
	}
}

int Sum(int i)   //求前i项的和
{
	int s = 0;
	//将前i项分段
	while(i > 0)
	{
		s += sum[i];
		i -= lowbit(i);  //去掉i的二进制最后一个
	}
	return s;
}


以下数组下标均默认从1开始

应用一

假如给你一个数组a[ ] = {2,5,3,4,1},b[i]b[i] 表示在a[1],a[2]...a[i-1](即位置i左边)小于等于a[i]的数的个数。对此例b[] = {0,1,1,2,0}。 那么该如何去求得b[i]呢?

解法:假如要得到b[4]的值,对于a[4] = 4. 我们 只要得到在a[1],a[2],a[3] 中出现小于等于4的个数,即1,2,3,4的个数,此例即为2. a[1] = 2 < a[4], a[3] = 3 < a[4]. 所以b[4] = 2;其他的以此类推b[i]的值,需要得到在a[1],a[2]....a[i-1]中出现小于等于a[i]的个数,即1,2...a[i]的个数相当于求前a[i]项的和,可用到树状数组

具体操作

for(int i=1; i<=n; i++)

{

b[i] = getSum(a[i]); //求前a[i]项的和

update(a[i],1);      //a[i]个元素+1

}

应用二

假如给你一个数组a[ ] = {2,5,3,4,1},b[i]b[i] 表示在a[1],a[2]...a[i-1](即位置i左边)大于等于a[i]的数的个数。对此例b[] = {0,0,1,1,4}。 那么该如何去求得b[i]呢?

解法1只需要先将数组a倒过来编号,即将a转换为,a[] ={4,1,3,2,5}.此时具体的操作如应用一

解法2:改变更新路径和求和路径


void update(int x, int val)
{
	for(int i=x; i>0; i-=lowbit(i))
	{
		sum[i] += val;
	}
}

int getSum(int x)
{
	int s = 0;
	for(int i=x; i<MAXN; i+=lowbit(i))
	{
		s += sum[i];
	}
	return s;
}

 

应用三  逆序数

假如给你一个数组a[ ] = {2,5,3,4,1},b[i]b[i] 表示在a[i],a[i+1]...a[n](即位置i右边)小于等于a[i]的数的个数。对此例b[] = {1,3,1,1,0}。 那么该如何去求得b[i]呢?

操作:应用一位置i的左边,应用三是位置i的右边。 然后只需要在应用一的基础上从后往前操作即可

for(int i=n; i>=1; i--)

{

b[i] = getSum(a[i]); //求前a[i]项的和

update(a[i],1);      //a[i]个元素+1

}

应用四

假如给你一个数组a[ ] = {2,5,3,4,1},b[i]b[i] 表示在a[i],a[i+1]...a[n](即位置i右边)大于等于a[i]的数的个数。对此例b[] = {3,0,1,0,0}。 那么该如何去求得b[i]呢?

操作:只需将数组a倒过来编号,即将a转化为 a[]={4,1,3,2,5} 然后利用应用三

 

二维树状数组

int lowbit(int x)
{
	return x&(-x);
}
void update(int x, int y, int val) //将 a[x][y] 的值增加val
{
	for(int i=x; i<N; i+=lowbit(i))
	{
		for(int j=y; j<N; j+=lowbit(j))
		{
			sum[i][j] += val;
		}
	}
}
int getSum(int x, int y) //求以1,1为左上角端点,学校,x,y为右下角端点的矩阵和.
{
	int s = 0;
	for(int i=x; i>0; i-=lowbit(i))
	{
		for(int j=y; j>0; j-=lowbit(j))
		{
			s += sum[i][j];
		}
	}
	return s;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值