树状数组:
1、 一个可以动态维护前缀和的一个数据结构,支持求和和单点修改。在拓展情况下可以支持区间修改。
2、 优点:代码短,O(lgn)的开销
lowbit是非常有用的 x&(-x) 利用补码的性质得出最低位的数字。
核心代码:
inline int lowbit(int x){ return x&(-x);}
void add(int x,int val)
{
/* 修改 a[x] */
for (int i = x ; i < maxn ; i += lowbit(i) )
node[i] += val;
}
int get(int x){
/* 求a[1] + a[2] + ... a[x] */
int sum = 0;
for (int i = x ; i ; i -= lowbit(i))
sum += node[i];
return sum;
单点修改RMQ就不讲了实在是太容易了。
POJ 2352
给出按照y升序,y相同x升序的有序数列,
分别统计: 在某个坐标左下(包括左、下)有1~n个坐标的个数
分析:
1、 当y相同时,很明显,求在这个区间内的和就可以了。
2、 当y大,而x小的时候:我们求的是什么?想一想,也就是我们求的时之前横坐标值为1~x的坐标总数,从这个道理上讲,求(y,x)和求(y+1,x)并没有本质区别。因此,我们只要像1一样求和就可以了。
3、求和结束后插入新的节点,值位y
4、在维护前缀的时候有不干涉后面情况的性质,因此可以使用树状数组
#include <iostream>
#include <cstring>
using namespace std;
#define maxn 32222
int node[maxn];
int levels[maxn];
inline int lowbit(int x){ return x&(-x);}
void add(int x,int val)
{
/* 修改 a[x] */
for (int i = x ; i < maxn ; i += lowbit(i) )
node[i] += val;
}
int get(int x){
/* 求a[1] + a[2] + ... a[x] */
int sum = 0;
for (int i = x ; i ; i -= lowbit(i))
sum += node[i];
return sum;
}
int main(){
int n,x,y;
while(~scanf("%d",&n)){
memset(levels,0,sizeof(levels));
int tt=n;
while(tt--){
scanf("%d%d",&x,&y);
x++;
levels[get(x)]++;
add(x,1);
}
for(int i=0;i<n;i++) printf("%d\n",levels[i]);
}
}