正题
大意
求一个闭区间内二进制0的个数大于等于1的个数的数的数量。
解题思路
我们将二进制考虑成01串
我们先不考虑有前导零的情况:
在kk个空位中放个0,别的都放1的方案数为CikCki(我们将一个位置放0表示成取出这个位置的数)。
然后我们可以先计算1∼r1∼r,然后减去1∼l−11∼l−1就是l∼rl∼r这个区间内的数量。
我们考虑计算:
如一个数1212,我们将它转换为二进制1001010010。我们先计算1∼100001∼10000中的数量,我们可以先枚举位数,然后我们保证第一位是1后后面的就是在剩余空位中放置满足个数要求的情况。
然后我们开始考虑后面的,我们可以发现如果我们从大到小枚举到一位数是11,那么如果我们将其改为然后后面无论如何填入都无需考虑大小,所有我们可以利用这个性质计算,如果是11就该为统计答案,之后该回11<script type="math/tex" id="MathJax-Element-2245">1</script>继续往后(因为这个位数是0的情况都计算完了)。
Code
#include<cstdio>
#include<cmath>
#define ll long long
#define find_c(x,y) c[x+1][y+1]
#define w 31
using namespace std;
ll first,second,c[w+2][w+2],f[w+2];
void make_C()
{c[1][1]=1;c[2][1]=1;c[2][2]=1;for (ll i=3;i<=w+1;i++)for (ll j=1;j<=i;j++)c[i][j]=c[i-1][j]+c[i-1][j-1];//预处理组合数
}
int answer(ll x,ll y)//计算在k个位中填01并且前面已经有y个1的合法情况总数
{int ans=0;for (int i=0;i<=x;i++)if (i>=x-i+y)//满足条件ans+=find_c(x,i);//计算答案return ans;
}
int work(ll x)
{if (x==0) return 0;ll k=log2(x)+1,ans=0,now=1,one=1;for (int i=1;i<k;i++)ans+=answer(i-1,1),now<<=1;//计算答案k--;now>>=1;//无视第一位(前面已经计算过了)while(k){if (x&now) ans+=answer(k-1,one-1),one+=1;//可以计算答案else one-=1;//统计零一个数k--;now>>=1;//下一位}if (one<=0) ans++;//判断本身是否满足要求return ans;
}
int main()
{make_C();scanf("%d%d",&first,&second);printf("%d",work(second)-work(first-1));
}