也具体不知道能不能算作数学题,姑且就归做数位DP了,各种小细节卡了半天
题意是让求区间【a,b】中有多少个符合要求的数,
所谓要求就是这个数的二进制下的0的个数要大于等于1的个数;
这种区间题首先想到的是用【1,b】的合格数减去【1,a-1】的合格数,这样我们就只需要找1->小于等于某一个数的所有合乎要求的数就行了。
假设一个数的二进制位是 1011001,十进制下是89,这个数的二进制位数是7那么所有二进制位数小于7的数并且符合要求的数都可以让【1,89】这个区间的SUM(要求数的总和)+1,这个用排列组合就行
然后找7位的小于这个数的并且符合要求的数,那么只要89的任意一个二进制下的1变成零,那么这个1之后的数位无论是0还是1生成的数都要比89小,所以我们假设我们让第四个1变成0
现在的数就变成了1010xxx,然后枚举xx中的零的个数算组合就行。
最后再判断一下这个数本身是不是符合要求。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
int c(int x,int y)
{
if (x<0||y<0) return 0;
if (x==0||y==0) return 1;
int tmp=1;
for (int i=1;i<=y;i++)
tmp=tmp*(x-i+1)/i;
return tmp;
}
int work(int x)
{
if (x==0) return 0;
int tmp_ans=0,tmp=x,now_0=0,now_1=0,tmp2_x=x,test;
int tmp_sqr=1;
long long now=0;
while (tmp!=0)
{
now++;
tmp_sqr=tmp_sqr<<1;
tmp/=2;
}
tmp_sqr=tmp_sqr>>1;
{
now_1=1;
now_0=0;
for (int j=now-1;j>0;j--)
{
test=x&(1<<(j-1));
if (test>0)
{
now_0++;
for (int k=((j-1)-now_0+now_1+1)/2;k<=j-1;k++)
tmp_ans+=c((j-1),k);
now_0--;
now_1++;
continue;
}
now_0++;
}
}
for (int i=now-1;i>=1;i--)
for (int j=((i+1)/2);j<=(i-1);j++)
tmp_ans+=c(i-1,j);
now_1=0;
now_0=0;
while (tmp2_x!=0)
{
if (tmp2_x%2==1) now_1++;
else now_0++;
tmp2_x=tmp2_x>>1;
}
if (now_0>=now_1) tmp_ans++;
return tmp_ans;
}
using namespace std;
int main()
{
freopen("poj3252.in","r",stdin);
freopen("poj3252.out","w",stdout);
int l,r,ansl,ansr;
scanf("%d%d",&l,&r);
ansl=work(l-1);
ansr=work(r);
printf("%d",ansr-ansl);
return 0;
}