JZOJ4711. Binary

题目大意

题目描述
Data Constraint
n,q105,ai<220

题解

和SCOI2016的题目有相似之处。
每一位开一个树状数组,以数轴为轴,记录只保留当前位往后的数有哪些。
对于每个询问,我们按位来统计答案,第 k 位对答案的贡献就是num(1<<k)其中 num 是+x后当前位为1的数的个数。
这个怎么查询呢?设当前统计第k位,之前的位置我们不需要考虑。注意到在没有+x的情况下,我们要求的数的下界是 L=k10000... 最高位的1是第 k 位的,上界是R=k11111...,现在有+x,所以真正要查询的区间是 [Lx,Rx] 。但是还有进位的情况,此时下界为 L=k+111000... k+1 k 位都是1,其余全0,上界为R=k+111111..., k+1 位全1。

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;

#define N 100000 + 10
#define M 1200000 + 10
typedef long long ll ;
const int Maxv = 1048576 ;

int T[20][M] ;
int a[N] ;
int n , Q ;
int ret ;
ll ans ;

int lowbit( int x ) { return x & (-x) ; }

void Modify( int t , int k , int del ) {
    while ( k <= Maxv + 1 ) {
        T[t][k] += del ;
        k += lowbit(k) ;
    }
}

int Find( int t , int k ) {
    int ret = 0 ;
    if ( k > Maxv ) k = Maxv ;
    while ( k > 0 ) {
        ret += T[t][k] ;
        k -= lowbit(k) ;
    }
    return ret ;
}

int main() {
    scanf( "%d%d" , &n , &Q ) ;
    for (int i = 1 ; i <= n ; i ++ ) {
        scanf( "%d" , &a[i] ) ;
        for (int k = 0 ; k <= 19 ; k ++ ) {
            int t = (a[i] & ((1 << (k+1)) - 1)) ;
            Modify( k , t + 1 , 1 ) ;
        }
    }
    for (int i = 1 ; i <= Q ; i ++ ) {
        int op , x , y ;
        scanf( "%d%d%d" , &op , &x , &y ) ;
        if ( op == 1 ) {
            for (int k = 0 ; k <= 19 ; k ++ ) {
                int t = (a[x] & ((1 << (k+1)) - 1)) ;
                Modify( k , t + 1 , -1 ) ;
            }
            a[x] = y ;
            for (int k = 0 ; k <= 19 ; k ++ ) {
                int t = (a[x] & ((1 << (k+1)) - 1)) ;
                Modify( k , t + 1 , 1 ) ;
            }
        } else {
            ans = 0 ;
            for (int k = 19 ; k >= 0 ; k -- ) {
                if ( !((y >> k) & 1) ) continue ;
                int tp = (x & ((1 << (k+1)) - 1)) ;
                int L = (1 << k) - tp ;
                int R = (1 << (k+1)) - 1 - tp ;
                ret = Find( k , R+1 ) - Find( k , L ) ;
                L = ((1 << k) | (1 << (k+1))) - tp ;
                R = (1 << (k+2)) - 1 - tp ;
                ret += Find( k , R+1 ) - Find( k , L ) ;
                ans += (ll)ret * (1ll << k) ;
            }
            printf( "%lld\n" , ans ) ;
        }
    }
    return 0 ;
}

以上.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值