在找树状数组模板题时找到的这道题。看了题目后我发现这是区间更新单点查询的题目。回想起来树状数组不是区间查询单点更新用的吗?难道反着也行?但就是有人这么写过了。有意思。
我们来分析一下:
下面的分析要用到之前讲树状数组原理的一些结论,见 https://blog.csdn.net/fire_lch316/article/details/100167665

区间更新:
还是上的这副老图来分析。记不记得线段树的区间更新,那是用了一个延迟更新的方法降低复杂度的。比如我现在要把数组1到5的每个单元都加上1。那么只用在上图4和5单元各加上一,表示以4为根的子树所有叶子节点都加了个1,及以5为根的子树所有叶子节点都加了个1。
引申一下,那么如果要把数组1到i每个单元都加上1,对应上图是什么操作呢?还是用延迟,就是说把覆盖了1到i叶节点的所有子树的每个子树的根节点加一就行了(其实在图上看和线段树操作的意义一样)。而遍历每个子树的根节点不正是对应了树状数组的向下查询的遍历过程吗。即i=i-(i&(-i))。
单点查询:
那么查询操作是怎么回事呢?
现在如果查询数组3单元的值,就是说要把所有包含上图最底层3位置叶节点的子树的每个子树根节点的延迟都加上来,就是说找到并加上3的所有实心祖先节点的值。对应树状数组不正是向上更新的遍历过程吗?即i=i+(i&(-i))。
上程序:
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <queue>
using namespace std;
typedef long long ll;
const int max_n=1e5+10;
int A[max_n];
int n;
int get_k(int x)
{
return x&(-x);
}
void add(int i,int x)
{
while(i>0)
{
A[i]+=x;
i=i-get_k(i);
}
}
int query(int i)
{
int res=0;
while(i<=n)
{
res+=A[i];
i=i+get_k(i);
}
return res;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
memset(A,0,sizeof(A));
int a,b;
for(int i=0;i<n;i++)
{
scanf("%d%d",&a,&b);
add(b,1);
add(a-1,-1);
}
for(int i=1;i<=n;i++) printf("%d%c",query(i),i==n?'\n':' ');
}
}
本文深入解析了树状数组在区间更新与单点查询的应用,通过对比线段树的操作,阐述了树状数组如何实现这两种操作。文章详细解释了更新和查询的过程,并提供了完整的代码实现。
1391

被折叠的 条评论
为什么被折叠?



