JZOJ 4711 Binary【NOIP2016提高A组模拟8.17】

本文介绍了一种使用二进制和树状数组解决特定数值范围内查询问题的方法。通过将输入数据转换为二进制形式,并利用树状数组进行高效查询与更新,解决了给定数值范围内的查询问题。

Binary

题目描述

这里写图片描述

数据范围

这里写图片描述

题解

看到数据范围这里写图片描述
这暗示着我们要把ai拆成20位的二进制来做。
如果ai化成二进制后第j位上是1,那么必满足
2j<=ai mod 2j+1<=2j+1-1 (显然,想一想不等式代表的含义就知道为什么,请读者自行思考)

询问时就是问有多少个ai满足2j<=(ai+x) mod 2j+1<=2j+1-1
也就是问有多少个ai满足-x+2j<=ai mod 2j+1<=-x+2j+1-1(暂且不算不等式两边的值为负数的情况)
那么对答案的贡献就为满足上述不等式的ai的个数kj乘上2j
kj怎么算呢?
kj也就等于满足ai mod 2j+1<=-x+2j+1-1ai个数减去满足ai mod 2j+1<-x+2jai个数。
这个可以种20棵树状数组来维护。

那接下来剩下的问题就是不等式两边的值为负数的情况。
首先,有一个数uu and 2i的运算结果是随着u的增长循环出现,2i个0和2i个1轮流出现。
因为有可循环性,所以我们可以像下图一样处理:
这里写图片描述
被圈住的区间是生效的区间,如果过了界就把过界区间往后面放,再求相应区间的答案就可以了。

Code(Pascal)

var
    ans:int64;
    m2:array[0..20] of int64;
    n,q,i,j,l,o,p,op,ll,rr:longint;
    a:array[0..200000] of longint;
    tr:array[0..20,0..2000000] of int64;
    k:array[0..20] of longint;
procedure zj(o,lyh:int64);
    var
        i:longint;
    begin
        for i:=0 to 20 do
        if o and m2[i]>0 then k[i]:=k[i]+lyh;
    end;
function lowbit(o:longint):longint;
    begin
        exit(o and (-o));
    end;
function find(p,l,r:longint):int64;
    var
        uu:int64;
    begin
        inc(r);
        uu:=0;
        while r>0 do
        begin
            uu:=uu+tr[p,r];
            r:=r-lowbit(r);
        end;
        while l>0 do
        begin
            uu:=uu-tr[p,l];
            l:=l-lowbit(l);
        end;
        exit(uu);
    end;
procedure zj(p,cty,k:longint);
    begin
        inc(cty);
        while cty<=m2[p+1] do
        begin
            tr[p,cty]:=tr[p,cty]+k;
            cty:=cty+lowbit(cty);
        end;
    end;
begin
    readln(n,q);
    m2[0]:=1;
    for i:=1 to 20 do
    m2[i]:=m2[i-1]*2;
    for i:=1 to n do
    read(a[i]);
    for i:=1 to n do
    for l:=0 to 19 do
    zj(l,a[i] mod m2[l+1],1);
    for i:=1 to q do
    begin
        readln(op,o,p);
        if op=2 then
        begin
            ans:=0;
            for l:=0 to 19 do
            if p and m2[l]>0 then
            begin
                ll:=((-o+m2[l]) mod m2[l+1]+m2[l+1]) mod m2[l+1];
                rr:=((-o+m2[l+1]-1) mod m2[l+1]+m2[l+1]) mod m2[l+1];
                if ll>rr then ans:=ans+(find(l,0,rr)+find(l,ll,m2[l+1]-1))*m2[l]
                else ans:=ans+find(l,ll,rr)*m2[l];
            end;
            writeln(ans);
        end
        else
        begin
            for l:=0 to 19 do
            begin
                zj(l,a[o] mod m2[l+1],-1);
                zj(l,p mod m2[l+1],1);
            end;
            a[o]:=p;
        end;
    end;
end.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值