NOIP提高模拟 Binary

来源:互联网 发布:mac系统怎样翻墙 编辑:程序博客网 时间:2024/06/05 01:14

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

我们考虑到询问and、or、xor一个数之类的问题,通常都是将二进制上的位分开来处理。想到这个就好做了。我们开20颗树状数组,第i颗树状数组记录1~n中的每个数保留后i位之后,权值在0~2i1的数的个数,也就是个权值线段树。对于一个修改操作,我们只需将原本ax在树状数组中的值的位置上减1,在y在树状数组中的位置加1,更新一下ax即可。

而对于查询操作,我们考虑第i位对于答案的贡献。第i位对于答案的贡献就是区间在2i1~2i-1的数的个数*2i1(其中y的第i位必须为1)。但此时多了个x对答案产生了影响,所以我们将区间调整为2i1x~2i1x(其中x保留后i位)。我们可能会问,当2i1< x时怎么处理?我们发现,设k=x-2^{i-1},2i1k~2i12i1x~0是等价的。因为2i1k~2i1里的每个数+x后导致进位,而我们并不关心进位的那一位,我们只关心剩余下来的i位,这就相当于算法中的取模,所以2i1k~2i1里的每个数+x后对第i位都是有贡献的,因为他们第i位都有值。

所以,这个算法的复杂度就降为O(Nlog2 n)。

代码

#include<iostream>#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>#define ll long longusing namespace std;const int maxn=100005,maxn1=21,maxn2=1048580;int a[maxn],n,m,i,t,k,j,l,x,f[maxn1+3][maxn2],ch,y,z1;long long ans;int lowbit(int x){    return x & (-x);}void insert(int z,int x,int y){    if (x>z1) return;    f[z][x]+=y;    insert(z,x+lowbit(x),y);}ll find(int z,int x){    if (x<1) return 0;    return f[z][x]+find(z,x-lowbit(x));}int main(){    //freopen("data.in","r",stdin);freopen("data.out","w",stdout);    scanf("%d%d",&n,&m);    for (i=1;i<=n;i++){        scanf("%d",&a[i]);t=a[i];        for (j=maxn1;j>=1;j--){            if (t>(1<<j)-1) t=t-(1 << j);            z1=(1<<j);            insert(j,t+1,1);        }    }    for (i=1;i<=m;i++){        scanf("%d%d%d",&ch,&x,&y);        if (ch==1){            t=a[x];            for (j=maxn1;j>=1;j--){                if (t>(1<<j)-1) t=t-(1 << j);                z1=(1<<j);                insert(j,t+1,-1);            }            t=y;            for (j=maxn1;j>=1;j--){                if (t>(1<<j)-1) t=t-(1 << j);z1=(1<<j);                insert(j,t+1,1);            }            a[x]=y;        }else{            ans=0;            for (j=maxn1;j>=1;j--)                if ((1<<(j-1))&y){                    x=x & ((1 << j)-1);                    t=1<<(j-1);k=(1<<j)-1;z1=1<<(j-1);                    t=(t+(1<<j)-x)%(1<<j);k=(k-x+(1<<j))%(1<<j);                    if (t<=k) ans+=(find(j,k+1)-find(j,t))*z1;                    else ans+=(find(j,k+1)+find(j,1<<j)-find(j,t))*z1;                }            printf("%lld\n",ans);        }    }}
2 0
原创粉丝点击