曾经,离开的时候,utoppia同学和我打赌说我的POJAC题数不会超过300。
现在,我又回来,固执地做着没落的POJ,只为那个约定。你却再没有机会和我并肩作战。
不久,我们队又将出发,带着utoppia的名字,继续未完成的梦。
废话完毕。
Poj3252.Round Numbers。
将数化成二进制,问在[start,finish]区间内的数满足0的位数大于等于1的位数的数有多少个。
对于这种区间问题,显然可以变成[1,finish]-[1,start-1]来处理。
对于一个数N,[1,N]区间的满足要求的数怎么求呢。
举个例子,对于1010001这样的数,可以这样分类:
长度等于N,从某一位(1..l-1)开始小于N的数。
XXXX..代表任意0、1串。
分成这三类后,只要在XXXX..中取得足够多的0,使得总的0数量大于1的数量即可。
通过组合数可以计算XXXX中选得足够多的0的的取法。
现在,我又回来,固执地做着没落的POJ,只为那个约定。你却再没有机会和我并肩作战。
不久,我们队又将出发,带着utoppia的名字,继续未完成的梦。
废话完毕。
Poj3252.Round Numbers。
将数化成二进制,问在[start,finish]区间内的数满足0的位数大于等于1的位数的数有多少个。
对于这种区间问题,显然可以变成[1,finish]-[1,start-1]来处理。
对于一个数N,[1,N]区间的满足要求的数怎么求呢。
举个例子,对于1010001这样的数,可以这样分类:
1.
1
1X
1XX
1XXX
1XXXX
1XXXXX
2.
1010001
3.
1010000
长度等于N,从某一位(1..l-1)开始小于N的数。
XXXX..代表任意0、1串。
分成这三类后,只要在XXXX..中取得足够多的0,使得总的0数量大于1的数量即可。
通过组合数可以计算XXXX中选得足够多的0的的取法。
组合数预处理得到。分类的第一种数也可以预处理得到。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define NN 50
long long c[NN][NN];
void init(int n){
int i,j;
memset(c,0,sizeof(c));
for(i=0;i<=n;++i){c[i][0]=c[i][i]=1;}
for(i=1;i<=n;++i){
for(j=1;j<i;++j){
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
}
int b[NN],sum0[NN];
long long work(int n){
if (n==0) return 0;
int l=0,i,j,lb,tmp,tl;
long long ret=0;
while(n){
b[++l]=n%2;
n=n/2;
}
sum0[l+1]=0;
for(i=l;i>=1;--i){
sum0[i]=sum0[i+1]+(b[i]==0?1:0);
}
for(i=1;i<l;++i){ //1
for(j=(i+1)/2;j<=i-1;++j){
ret+=c[i-1][j];
}
}
lb=(l+1)/2; //2
if (sum0[1]>=lb) ret++;
for(i=1;i<l;++i){ //3
if (b[i]==1){
tmp=sum0[i+1]+1;
tl=i-1;
for(j=max(lb-tmp,0);j<=tl;++j){
ret+=c[tl][j];
}
}
}
return ret;
}
int main(){
init(33);
int st,ed;
long long ans;
while(scanf("%d%d",&st,&ed)!=EOF){
st--;
ans=work(ed)-work(st);
printf("%I64d\n",ans);
}
return 0;
}