POJ-3252-Round Numbers-组合数学-递推

http://poj.org/problem?id=3252

本题 给出a,b

求【a,b】之间的所有round number

什么是round number?  就是一个数的二进制表示下,0的个数>=1的个数


这篇文章有详细的推导过程:http://www.cnblogs.com/lyy289065406/archive/2011/07/31/2122758.html



主体思路就是 把求区间【a,b】的RN数,转为求 小于k 的RN数的个数

然后只需要求 RN(b+1)-RN(a)即可得到题目答案


而求小于k的RN数,可分为两步:   设k的二进制有效位为len位

1:求长度小于len的RN数,长度小于len的数必然小于k

显然这部分好求,我们枚举每一个长度x,对该长度的RN数,便是 C【x-1】【y】,C【x-1】【y+1】....C【x-1】【x-1】,判断一下y的值即可


2:求长度等于len,值小于k的数

我们从次高位开始遍历,如果i位是1,那么当i位为0的时候,不管后面怎么变都肯定比k小,计算出当前第i位为0,i位以后任意排列的RN数(需要记录前面出现了几个0)

如果第i位为0,则不作处理,(因为后面会被计算上)


至于用到的组合数最大不超过31,打表法即可解决


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iostream>
using namespace std; 
__int64 inf=15;
double eps=0.000001;    
__int64 n,m;   
 __int64 cc[100][100];
 void pre()
 {
	 __int64 i,j;
	 for (i=0;i<=40;i++) 
		 cc[i][0]=1; 
	 for (i=1;i<=40;i++) 
		 for(j=1;j<=40;j++) 
			 cc[i][j]=(cc[i-1][j-1]+cc[i-1][j]); 
 }
 __int64 C(__int64 i,__int64 j)
 {
	return cc[i][j];
 }
 __int64 cal(__int64 x)//计算比x小的Round Num
 {
	 __int64 i,j;
	 __int64 tmp=x;__int64 cun=0; 
	 while(tmp)
	 { 
		 tmp/=2;cun++;
	 }
	 __int64 sum=0;	
	 for (i=1;i<cun;i++)	//计算长度小于len_X的RNum
	 {
		 for(j=(i-1)/2+1;j<i;j++)
		 {
			sum+=C(i-1,j);
		 }
	 }
	 //计算长度等于len_x的RN
	 __int64 zero=0;//统计从高到低遇到的0的个数
	for (i=cun-1;i>=1;i--)
	{
		__int64 tmp=x&(1<<(i-1));
		if (tmp)	//如果i位是1,那么当i位为0的时候,肯定比x小,计算出他们
		{
			for(j=(cun+1)/2-zero-1;j<=i-1;j++)
			{
				if (j<0) continue;//不存在拥有当前指定零的个数的比x小的数存在

				sum+=C(i-1,j);
			}
		}
		else
		{
			zero++; //如果i位为0,计数+1,比x小的数留给后面的算,本处不处理
		}
	} 
return sum;
 }
int main() 
{ 
	pre();
	 scanf("%I64d%I64d",&n,&m);
	__int64 t1=cal(n);
	__int64 t2=cal(m+1);
	printf("%I64d\n",t2-t1 );
	return 0;
}


你可能感兴趣的:(POJ-3252-Round Numbers-组合数学-递推)