//POJ 2352
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std ;
const int maxn = 15010 ;
const int maxx = 32010 ;
/*
树状数组是一个在线算法,相较于离线算法他不需要提前知道所有的输入, 他可以来一个输入处理一个输入
树状数组维护的是一个数组,这个数组具有树的性质
树状数组c[i] = A[i] + A[i-1] + ... A[i- 2^k + 1], k等于i的二进制数的末尾0的个数 , 2^k可以通过 i&(-i) 求得
也就是c[i] 表示A序列[ i - 2^k + 1 , i ] 的区间和
利用c数组下标i的二进制性质,c[i] 可以写成多个c[x]+ ... c[y] + A[i] 的形式
这样就能够表现出树状数组树的性质了
例如c[8] = A[8] + A[7] + A[6] + A[5] + A[4] + A[3] + A[2] + A[1]
= A[8] + c[7] + c[6] + c[4]
复杂度:构建树状数组复杂度nlogn , 修改节点复杂度logn , 查询logn
应用:区间和,区间前缀和
*/
int cnt[maxn] ;
//树状数组
int c[maxx] ;
//2^k,也就是c[i]表示的区间和的区间范围大小
int lowbit( int x ){
return x & ( - x ) ;
}
//更新节点
void add( int i , int val){
//更新A区间第i点值,同时树状数组也要更新,对于i所在的树分支上的路径的所有节点都要更新
while( i <= maxx ){
//更新i节点
c[i] += val ;
//i节点的父节点为 i + lowbit(i) ,通过演绎可以看到这个很正确
i += lowbit(i) ;
}
}
//求A区间[1,i]的区间前缀和
int sum( int i ){
int s = 0 ;
while( i > 0){
//i前缀和可以划分成多个c[x] 的和 , 每个c[x] 都是各自区间的连续和
s += c[i] ;
//i从大到小不断取i的子树区间c[x]
i -= lowbit(i) ;
}
//i的前缀和s
return s ;
}
int main(){
int n , x , y ;
while( scanf("%d" , & n ) != EOF){
memset( c , 0 , sizeof( c)) ;
memset(cnt , 0 , sizeof( cnt)) ;
for( int i = 0 ; i<n ;i++){
scanf("%d%d" , & x , & y ) ;
//防止x为0
//x之前的和表示temp层次
int temp = sum( x + 1) ;
cnt[temp ] ++ ;
//每个星星的权都是1
add( x + 1 , 1) ;
}
for(int i = 0 ; i < n ;i++)
printf("%d\n" ,cnt[i] ) ;
}
return 0 ;
}
树状数组
最新推荐文章于 2023-06-21 22:43:57 发布