树状数组

//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 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值