Binary

来源:互联网 发布:淘宝首页怎么弄全屏 编辑:程序博客网 时间:2024/05/17 23:48

题目大意

这里写图片描述

拆位

涉及位运算的操作,通常需要拆位,不过这题稍有不同,因为括号里有个+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);        }    }}
1 0
原创粉丝点击