您的位置:首页 > 其它

POJ 3252 Round Numbers (组合数学/数位dp)

2017-03-06 22:39 330 查看

题目链接

POJ3252

题目大意

求给定整数区间内有多少个“round number”.

“round number”指是指二进制形式下0的个数不少于1的个数的整数。

分析

首先这是一道区间统计问题,求[a,b]之间RN个数,答案即为b+1以内RN个数减去a以内RN个数(我这里说的以内指严格小于)。

因此,问题就转化求比整数n小的RN个数,用函数Count(n)表示。(n≤ 2,000,000,000)

计算时,首先我们需要将n转化成二进制,用bin[]数组存储,长度用bin[0]存储。

然后进行分类计算:

1.若是二进制长度比dec(n)短的数,那它一定比n小,那么

sum1=∑bin[0]−1i=1f(i),f(i)表示长度为i的RN个数

而f(i)=∑i−1j=i/2+1Cji−1

i-1是因为最高位一定是1,因此可以放0的个数最多为i-1,

i/2+1是若要成为RN,至少需要放0的个数

2.若是与dec(n)位数相等的数,则需保证该位数下枚举出的数要比n小,处理方法是:从高位到低位搜索过程中,遇到当前位为0,则不处理,但要用计数器zero累计当前0出现的次数,遇到当前位为1,则先把它看做为0,zero+1,那么此时当前位 后面的 所有低位任意组合都会比k小,找出这些组合中RN的个数,统计完毕后把当前位恢复为原来的1,然后zero-1,继续向低位搜索。这样计算出sum2

Count(n)=sum1+sum2

最后还需解决组合数的计算问题,由于这里二进制位数最多大概在30位左右,范围比较小,因此可以借助杨辉三角预处理打表求得组合数。

组合数计算问题详见大神博文:组合数取模

代码

#include <iostream>
#include <cmath>
#define MAXN 35
using namespace std;
int c[MAXN][MAXN];
int bin[MAXN];
void Make_C()//利用杨辉三角求组合数
{
for (int i=0;i<=MAXN;i++)
for (int j=0;j<=i;j++)
if (!j||i==j)
c[i][j]=1;
else
c[i][j]=c[i-1][j-1]+c[i-1][j];
}
void Dec_to_Bin(int n)//将n转换成二进制
{
bin[0]=0;
while (n)
{
bin[++bin[0]]=n&1;
n>>=1;
}
}
int Count(int n)//计算出小于n的RN个数
{
int sum=0,i,j,zero;
Dec_to_Bin(n);
/*计算二进制长度比Dec(n)的长度小的数中的RN个数*/
for (i=1;i<bin[0]-1;i++)
for (j=i/2+1;j<=i;j++)
sum+=c[i][j];
/*计算二进制长度等于Dec(n)的长度的数中的RN个数*/
zero=0;//记录当前已出现0的个数
for (i=bin[0]-1;i>=1;i--)//从高位向地位扫
if (bin[i])
for (j=(bin[0]+1)/2-(zero+1);j<=i-1;j++)
sum+=c[i-1][j];
else
zero++;
return sum;
}

int main()
{
int a,b;
Make_C();
cin>>a>>b;
cout<<Count(b+1)-Count(a)<<endl;
return 0;
}


PS:看了大神的博客知道这题还可以用数位DP做,待续吧。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj usaco 组合数 数位