题意:求[start,finish]区间内,二进制满足0的个数大于1的个数的数有多少个?(1 ≤ Start < Finish ≤ 2,000,000,000).
分析:排列组合的题,再用一点数位DP的思想
只需求解 [0,n) 区间内满足条件的个数。
做法: 初始化 f [ i ] [ j ] ,表示二进制 i 位数中有j个1的数的个数(相当于组合数c( i, j ))。
将n转化成二进制,首先考虑首位取零,然后枚举第一个1出现的位置(决定了该数的长度),然后加上满足j<=new_len/2-1的 f[ i ][ j ]
再考虑首位取1,则该数长度一定为原始len,然后依次考虑为1的数位,将改为变为0,然后加上满足j<=len/2-cnt的f [ i ][ j ](cnt表示n在该位之前1的个数)
另加判断,如果cnt>len/2,则不再枚举。因为不可能满足条件。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
int f[35][35];//i位数,有j个1
int cal(int n)
{
int i,j,k,sum=0,len=0,num[35],cnt=1;
num[0]=0;
while(n)
{
num[len]=n%2;
len++;
n>>=1;
}
for(i=len-2;i>=0;i--)
for(j=0;j<=(i+1)/2-1;j++)
sum+=f[i][j];
for(i=len-2;i>=0;i--)
{
if(num[i]==1)
{
if(cnt>len/2)
break;
else
{
for(j=0;j<=len/2-cnt;j++)
sum+=f[i][j];
}
cnt++;
}
}
return sum;
}
int main()
{
int n,m,i,j,k;
for(i=0;i<35;i++)
{
f[i][0]=f[i][i]=1;
for(j=1;j<i;j++)
{
f[i][j]=f[i-1][j]+f[i-1][j-1];
}
}
scanf("%d %d",&n,&m);
printf("%d\n",cal(m+1)-cal(n));
return 0;
}