前言
一开始想着按二进制位倒着推统计贡献,每次讨论四种情况。
推着推着就发现了结论。
然而还是想复杂了。
直接值域就好了嘛。
题意
用若干个(至少一个)[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+1−1]
。
然后看第二类数,显然我们可以or出
[b,2d−1]
因此我们只用第一类数或第二类数可以or出
[b,2d+2k+1−1]
如果同时用第一类数和第二类数呢?你可以让第一类数用一个10……0,然后第二类数可以随意但不得小于b,因此可以or出
[2d+b,2d+1−1]
,然后比较值域取并即可。
#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);
}