[agc015d]A or...or B Problem

来源:互联网 发布:snmp trap 端口号 编辑:程序博客网 时间:2024/06/05 15:25

前言

一开始想着按二进制位倒着推统计贡献,每次讨论四种情况。
推着推着就发现了结论。
然而还是想复杂了。
直接值域就好了嘛。

题意

用若干个(至少一个)[A,B]中的数进行or操作能得到多少本质不同的数?

做法

先找到A和B最高一个不同的二进制位,设为d。
因为我菜下面我们都假设A是较大数。而且因为我懒接下来都用小写。
我们可以根据第d位是0还是1划分两类数。有1的是第一类数。
我们找到a第二个有1的位置k,我们知道选一堆第一类数进行or在k位之前的位都一样,然后我们发现你可以让一个是10……0,另一个是0?……?,这样可以构造第k位是1剩余随意的数,也可以不要那个10……0。于是我们可以用若干第一类数or出[2d,2d+2k+11]
然后看第二类数,显然我们可以or出[b,2d1]
因此我们只用第一类数或第二类数可以or出[b,2d+2k+11]
如果同时用第一类数和第二类数呢?你可以让第一类数用一个10……0,然后第二类数可以随意但不得小于b,因此可以or出[2d+b,2d+11],然后比较值域取并即可。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;ll two[70],a,b,ans,x,y,u,v;int i,j,k,l,t,n,m,d;int main(){    scanf("%lld%lld",&a,&b);    swap(a,b);    two[0]=1;    fo(i,1,62)  two[i]=(ll)two[i-1]*2;    fd(i,62,0){        if ((a&two[i])!=(b&two[i])) break;        if ((a&two[i])>0){            a^=two[i];            b^=two[i];        }    }    if (a==b){        printf("1\n");        return 0;    }    d=i;    fd(k,i-1,-1)        if (k==-1||(a&two[k])>0) break;    x=b;y=two[d]+two[k+1]-1;    u=two[d]+b;v=two[d+1]-1;    ans=y-x+1;    if (u>y) ans+=v-u+1;    else if (u<=y&&v>y) ans+=v-y;    printf("%lld\n",ans);}