poj 3252 Round Numbers

题目大意是:给你两个数n和m,计算从n到m的所有数的二进制中,0的个数大于1的个数的数的个数。

这题用组合数学比较简单,不过要小心,很容易超时的,我就悲哀的RE了两次。

对于一个长度为Len的二进制(最高位为1),如何求出他的RoundNumbers呢(假设为用R(len)来表达),分为奇数和偶数两种情况

1、奇数情况:在Len=2k+1的情况下,最高位为1,剩下2k位,至少需要k+1为0

用C(m,n)表示排列组合数:从m个位置选出n个位置的方法

R(len)=C(2k,k+1)+C(2k,k+2)+...+C(2k,2k).

由于 A:C(2k,0)+C(2k,1)+...+C(2k,2k)=2^(2k)

       B:C(2k,0)=C(2k,2k), C(2k,1)=C(2k,2k-1) ,,C(2k,i)=C(2k,2k-i)

于是  C(2k,0)+C(2k,1)+...+C(2k,2k)

= C(2k,0)+C(2k,1)+...+C(2k,k)+C(2k,k+1)+C(2k,K+2)+...+C(2k,2k)

= 2*R(len)+C(2k,k)

=2^(2k)

所以R(len)=1/2*{2^(2k)-C(2k,k)};

2. 偶数情况 len=2*k,类似可以推到 R(len)=1/2*(2^(2k-1));

然后输入n和m,用cal(m)-cal(n-1)即可;

代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

using namespace std;

int p[35],st[35];
int n,m;

int f(int n,int m)
{
    int i,j,s;
    if(m>n-m)m=n-m;
    s=1;j=1;
    for(i=n-m+1;i<=n;i++)
    {
        s*=i;
        while(j<=m&&s%j==0)
        {
            s=s/j;
            j++;
        }
    }
    return s;
}

void init()
{
    int i,j;

    memset(p,0,sizeof(p));
    p[1]=0;
    for (i=2;i<32;i++)
    for(j=1;j<=i-j;j++)
    p[i]+=f(i-1,j-1);
}

int cal(int n)
{
    if(n<2) return 0;
    int i,j,s,x;
    x=n;
    int len=1;
    while(x)
    {
        x=x/2;
        len++;
    }
    len--;
    for(i=len;i>0;i--)
    {
        st[i]=n%2;
        n=n/2;
    }
    /*for(i=1;i<=len;i++)
    printf("%d ",st[i]);
    printf("\n");*/
    s=1;
    int s1=1;
    int s0=0;
    for(i=1;i<len;i++)
    s+=p[i];
    for(i=2;i<=len;i++)
    if(st[i])
    {
        if(s0+len-i>=s1+1)s++;
        for(j=1;j<=len-i&&s1+j<=len-(s1+j);j++)
        s+=f(len-i,j);
        s1++;
    }
    else
    s0++;
    return s;
}

int main()
{
    init();
    cin>>n>>m;
    cout<<cal(m)-cal(n-1)<<endl;
    return 0;
}

 

原文地址:https://www.cnblogs.com/misty1/p/2455840.html