Binary

题目大意

这里写图片描述

拆位

涉及位运算的操作,通常需要拆位,不过这题稍有不同,因为括号里有个+x的操作。
我们可以把a[i]%2^i后加入第i个线段树里(权值线段树),然后对于一个&y的操作,我们就可以只考虑y的二进制下有1的位。
先忽略x
假如这一位为10000
那么找到对应的线段树,查询10000~11111的个数,我们就可以得到贡献了。
那么有x操作,naive的想法就是查询max(0,10000-x1)~11111-x1(x1=x%2^5)
然而如果x为1????,可能会出现a[i]+x的第5位都为1而进位,但是a[i]+x的后部分进位使得第五位仍然为1的情况,这个考虑起来也比较容易,对应的区间其实就是(10000*3-x1,11111)
用线段树常数较大(我是卡过的),推荐树状数组

代码

#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define ll long long 
using namespace std;
const int maxn=100000+5;
const int ma=1<<20;
int i,j,n,q,t,two[21];
int tree[20][ma*4],a[maxn];
bool b;
void put(int k,int l,int r,int a){
    if (b) tree[t][k]++;else tree[t][k]--;
    if (l==r)return;
    int m=(l+r)/2;
    if (a<=m) put(k*2,l,m,a);else put(k*2+1,m+1,r,a);
}
int find(int k,int l,int r,int a,int b){
    if (a>b) return 0;
    if (l==a&&r==b) return tree[t][k];
    int m=(l+r)/2;
    if (b<=m) return find(k*2,l,m,a,b);else
    if (a>m) return find(k*2+1,m+1,r,a,b);else
    return find(k*2,l,m,a,m)+find(k*2+1,m+1,r,m+1,b);
}
int main(){
    scanf("%d%d",&n,&q);
    two[0]=1;
    fo(i,1,20) two[i]=two[i-1]*2;
    fo(i,1,n) {
        scanf("%d",&a[i]);b=1;
        fo(t,0,19) put(1,0,two[t+1]-1,a[i]%two[t+1]);
    }
    fo(i,1,q){
        int o,x,y;
        scanf("%d%d%d",&o,&x,&y);
        if (o==1){
            b=0;fo(t,0,19) put(1,0,two[t+1]-1,a[x]%two[t+1]);
            a[x]=y;b=1;
            fo(t,0,19) put(1,0,two[t+1]-1,a[x]%two[t+1]);
        }else{
            ll ans=0;
            fo(t,0,19){
                if (two[t]&y) {
                    int x1=x%two[t+1];
                    ll s=find(1,0,two[t+1]-1,max(0,two[t]-x1),max(-1,two[t+1]-1-x1));
                    if (x&two[t]) s+=find(1,0,two[t+1]-1,two[t]*3-x1,two[t+1]-1);
                    ans+=(ll)two[t]*s;
                }
            }
            printf("%lld\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值