【转载】树状数组求区间和的一些常见模型

news/2025/2/9 1:48:56

原文地址:http://www.cppblog.com/MatoNo1/archive/2011/03/19/142226.html

树状数组在区间求和问题上有大用,其三种复杂度都比线段树要低很多……有关区间求和的问题主要有以下三个模型(以下设A[1..N]为一个长为N的序列,初始值为全0):

(1)“改点求段”型,即对于序列A有以下操作:

【1】修改操作:将A[x]的值加上c;

【2】求和操作:求此时A[l..r]的和。

这是最容易的模型,不需要任何辅助数组。树状数组中从x开始不断减lowbit(x)(即x&(-x))可以得到整个[1..x]的和,而从x开始不断加lowbit(x)则可以得到x的所有前趋。代码:

void ADD(int x, int c)
{
     for (int i=x; i<=n; i+=i&(-i)) a[i] += c;
}
int SUM(int x) { int s = 0; for (int i=x; i>0; i-=i&(-i)) s += a[i]; return s; }

操作【1】:ADD(x, c);

操作【2】:SUM(r)-SUM(l-1)。



(2)“改段求点”型,即对于序列A有以下操作:

【1】修改操作:将A[l..r]之间的全部元素值加上c;

【2】求和操作:求此时A[x]的值。

这个模型中需要设置一个辅助数组B:B[i]表示A[1..i]到目前为止共被整体加了多少(或者可以说成,到目前为止的所有ADD(i, c)操作中c的总和)。

则可以发现,对于之前的所有ADD(x, c)操作,当且仅当x>=i时,该操作会对A[i]的值造成影响(将A[i]加上c),又由于初始A[i]=0,所以有A[i] = B[i..N]之和!而ADD(i, c)(将A[1..i]整体加上c),将B[i]加上c即可——只要对B数组进行操作就行了。

这样就把该模型转化成了“改点求段”型,只是有一点不同的是,SUM(x)不是求B[1..x]的和而是求B[x..N]的和,此时只需把ADD和SUM中的增减次序对调即可(模型1中是ADD加SUM减,这里是ADD减SUM加)。代码:

void ADD(int x, int c)
{
     for (int i=x; i>0; i-=i&(-i)) b[i] += c;
}
int SUM(int x) { int s = 0; for (int i=x; i<=n; i+=i&(-i)) s += b[i]; return s; }

操作【1】:ADD(l-1, -c); ADD(r, c);

操作【2】:SUM(x)。

 


(3)“改段求段”型,即对于序列A有以下操作:

【1】修改操作:将A[l..r]之间的全部元素值加上c;

【2】求和操作:求此时A[l..r]的和。

这是最复杂的模型,需要两个辅助数组:B[i]表示A[1..i]到目前为止共被整体加了多少(和模型2中的一样),C[i]表示A[1..i]到目前为止共被整体加了多少的总和(或者说,C[i]=B[i]*i)。

对于ADD(x, c),只要将B[x]加上c,同时C[x]加上c*x即可(根据C[x]和B[x]间的关系可得);

而ADD(x, c)操作是这样影响A[1..i]的和的:若x<i,则会将A[1..i]的和加上x*c,否则(x>=i)会将A[1..i]的和加上i*c。也就是,A[1..i]之和 = B[i..N]之和 * i + C[1..i-1]之和。
这样对于B和C两个数组而言就变成了“改点求段”(不过B是求后缀和而C是求前缀和)。
另外,该模型中需要特别注意越界问题,即x=0时不能执行SUM_B操作和ADD_C操作!

代码:

void ADD_B(int x, int c)
{
     for (int i=x; i>0; i-=i&(-i)) B[i] += c;
}
void ADD_C(int x, int c) { for (int i=x; i<=n; i+=i&(-i)) C[i] += x * c; }
int SUM_B(int x) { int s = 0; for (int i=x; i<=n; i+=i&(-i)) s += B[i]; return s; } int SUM_C(int x) { int s = 0; for (int i=x; i>0; i-=i&(-i)) s += C[i]; return s; }
inline
int SUM(int x) { if (x) return SUM_B(x) * x + SUM_C(x - 1); else return 0; }

操作【1】:
ADD_B(r, c); ADD_C(r, c);
if (l > 1) {ADD_B(l - 1, -c); ADD_C(l - 1, -c);}

操作【2】:SUM(r) - SUM(l - 1)。

转载于:https://www.cnblogs.com/GBRgbr/p/3270891.html


http://www.niftyadmin.cn/n/545248.html

相关文章

一个感染型木马病毒分析(二)

作者&#xff1a;龙飞雪 0x1序言 前面已经对感染型木马病毒resvr.exe的病毒行为进行了具体的分析见一个感染型木马病毒分析&#xff08;一&#xff09;&#xff0c;但是觉得还不够&#xff0c;不把这个感染型木马病毒的行为的亮点进行分析一下就有点遗憾了。下面就针对该感染型…

“DllRegisterServer的调用失败”问题解决办法(转)

在日常的工作中&#xff0c;用regsvr32 命令注册dll组件是&#xff0c;会碰到模块"xxx.dll"已加载&#xff0c;但DllRegisterServer的调用失败。特别是再在xp的系统上能正确注册&#xff0c;但是在win7系统上却出现上述问题。 解决办法&#xff1a; 程序 - 附件 - 命…

一个感染性木马病毒分析(三)--文件的修复

一、 序言 前面的分析一个感染型木马病毒分析&#xff08;二&#xff09;中&#xff0c;已经将该感染性木马病毒resvr.exe木马性的一面分析了一下&#xff0c;下面就将该感染性木马病毒resvr.exe感染性的一面分析一下。 二、文件感染方式的分析 之前感染性木马病毒的分析中&…

高级UI晋升之常用View(三)下篇

更多Android高级架构进阶视频学习请点击&#xff1a;https://space.bilibili.com/474380680 本篇文章将从WebView来介绍常用View: 一、WebView介绍 Android WebView在Android平台上是一个特殊的View&#xff0c; 基于webkit引擎、展现web页面的控件&#xff0c;这个类可以被用…

今年已发生的安全并购

1. CA收购Veracode&#xff08;6.14亿美元&#xff09;今年3月&#xff0c;美国企业IT公司 CA Technologies 以6.14亿美元买下了应用安全及***测试公司Veracode。CA称&#xff0c;收购当时&#xff0c;将Veracode融入其产品组合将撑起它在开发运维市场的安全功能。Veracode在应…

Linux进程间通信——使用匿名管道

在前面&#xff0c;介绍了一种进程间的通信方式&#xff1a;使用信号&#xff0c;我们创建通知事件&#xff0c;并通过它引起响应&#xff0c;但传递的信息只是一个信号值。这里将介绍另一种进程间通信的方式——匿名管道&#xff0c;通过它进程间可以交换更多有用的数据。一、…

OD调试程序常用断点大全

常用断点 拦截窗口&#xff1a; bp CreateWindow 创建窗口 bp CreateWindowEx(A) 创建窗口 bp ShowWindow 显示窗口 bp UpdateWindow 更新窗口 bp GetWindowText(A) 获取窗口文本 拦截消息框&#xff1a; bp MessageBox(A) 创建消息框 bp MessageBoxExA 创建消息框 bp MessageB…

高级UI晋升之布局ViewGroup(四)

更多Android高级架构进阶视频学习请点击&#xff1a;https://space.bilibili.com/474380680 本篇文章将从LinearLayout、RelativeLayout、FrameLayout、AbsoluteLayout、GridLayout来介绍常用View布局: 一、UI的描述 对于Android应用程序中&#xff0c;所有用户界面元素都是由…