JZOJ4711. Binary

本文介绍了一种使用树状数组解决区间加法问题的方法,并通过一个具体的编程实例详细解释了如何针对每个询问按位统计答案。该方法特别适用于处理大量数据更新和查询操作的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

题目描述
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+1k位都是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、付费专栏及课程。

余额充值