poj 3252

组合数学,对于长度为l的二进制数n,先求出长度小于l的满足意义的数,这里长度为l的数,都是指二进制以1开头的长度为为l的数,讨论以0开头的数在这种方法下没什么意义,因为每个数都可确定的知道其长度,并且是以1开头的,也就是说这样的分类可以包括所有的数并且没有重叠。然后再求长度为为l的小于n的满足要求的书。然后再检查n看他本身是否满足条件。这样就求出了1~n中满足条件的数的个数,还有c++提交除以2用位运算来实现,不然会wa

#include <iostream>
#include <cstdio>
using namespace std;
int f[33][33],re[33];
int getV(int x)
{
    if(x<=1) return 0;
    int a[33],l=0,sum=0,c=0;
    while(x)
    {
        if(x%2==0) c++;
        a[l++]=x%2;
        x=x>>1;
    }
    int t=l/2;
    if(l%2) t++;
    if(c>=t) sum++;
    int p;
    int i,j;
    for(i=2;i<=(l-1);i++) sum+=re[i];
    for(i=l-2;i>=0;i--)
    {
        if(a[i])
        {
            for(j=t-1;j<=i;j++)
                sum+=f[i][j];
        }
        else t--;
    }
    return sum;
}
int main()
{
    int i,j;
    f[0][0]=1;
    for(i=1;i<=31;i++)
    {
        f[i][0]=1;
        for(j=0;j<i;j++)
            f[i][j+1]=f[i-1][j]+f[i-1][j+1];
        if(i%2) re[i]=((1<<(i-1))-f[i-1][(i-1)/2])>>1;
        else re[i]=(1<<(i-1))>>1;
    }
    int s,e;
    while(~scanf("%d%d",&s,&e))
    {
    int v1=getV(e);
    int v2=getV(s-1);
    printf("%d\n",v1-v2);
    }
    return 0;
}


原文地址:https://www.cnblogs.com/lj030/p/3065559.html