题目链接:https://www.luogu.com.cn/problem/AT2382
询问在 [ L , R ] [L,R] [L,R]中选取一个或多个数,将它们按位或后能得到多少种不同的结果。
1 ≤ L ≤ R < 2 60 1\leq L\leq R<2^{60} 1≤L≤R<260
我们先把高位的 L L L和 R R R都有的 1 1 1都删除,因为它们之间的数都有这些 1 1 1,显然没有用。
那么这样 L L L和 R R R的最高位就不同了,我们找到 R R R的最高位 2 k 2^k 2k。
此时对于 [ L , 2 k ) [L,2^k) [L,2k)中的数字都在 L L L中,而且它们怎么或都不能超过 2 k 2^k 2k,所以这部分的答案就是 2 k − L 2^k-L 2k−L。
然后让这部分的数或上 2 k 2^k 2k,此时我们就有两个部分 [ 2 k , R ] [2^k,R] [2k,R]和 [ L + 2 k , 2 k − 1 ) [L+2^k,2^{k-1}) [L+2k,2k−1)的数字。
先令 L = L + 2 k − 1 L=L+2^{k-1} L=L+2k−1,然后我们分类讨论一下:
时间复杂度: O ( log 2 n ) O(\log^2 n) O(log2n)
#include
#include
#include
#define ll long long
using namespace std;
ll A,B,ans;
signed main()
{
scanf("%lld%lld",&A,&B);
while(1){
if(B<=1){
ans+=B-A+1;
printf("%lld\n",ans);
return 0;
}
ll k=1ll;
while(k*2ll<=B)k=k*2ll;
if((A&k)&&(B&k))
{A-=k,B-=k,k/=2ll;continue;}
ans+=k-A;B-=k;k/=2ll;swap(A,B);
while(k){
if(A>B||A>=k){
ans+=k*2ll;
printf("%lld\n",ans);
return 0;
}
if(B<k)ans+=k,k/=2ll;
else{
ans+=k*2ll-B;
B=A;A=0;
break;
}
}
}
printf("%lld\n",ans);
return 0;
}