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

来源:互联网 发布:java 写二进制文件 编辑:程序博客网 时间:2024/04/30 15:19

题目链接

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)=i1j=i/2+1Cji1
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 35using 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做,待续吧。。。

0 0