POJ 3252 Round Numbers (组合数)

 解析来源:http://poj.org/showmessage?message_id=158333


  以sample为例子
    [2,12]区间的RoundNumbers(简称RN)个数:Rn[2,12]=Rn[0,12]-Rn[0,1]
    即:Rn[start,finish]=Rn[0,finish]-Rn[0,start-1]
    所以关键是给定一个X,求出Rn[0,X]
    现在假设X=10100100 
    这个X的二进制总共是8位,任何一个小于8位的二进制都小于X
    第一部分,求出长度为[0,7]区间内的二进制是RoundNumber的个数
        对于一个长度为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));
    第二部分,对于上面这个长度为8的例子:即X=10100100,首先如果本身是RoundNumbers,第二部分的结果总数+1
        第一部分已经将长度小于8的部分求出。现在要求长度=8的RoundNumber数目
        长度为8,所以第一个1不可改变
        现在到第二个1,如果Y是前缀如100*****的二进制,这个前缀下,后面取0和1必然小于X,已经有2个0,一个1,剩下的5个数字中至少需要2个0,
            所以把第二个1改为0:可以有C(5,2)+C(5,3)+C(5,4)+C(5,5)
        现在第三个1,也就是前最为101000**,同样求出,至少需要0个0就可,所以有C(2,0)+C(2,1)+C(2,2)个RoundNumbers
        。。。
        将所有除了第一个1以外的1全部变为0,如上算出有多少个RoundNumbers,结果相加(由于前缀不一样,所以后面不管怎么组合都是唯一的)

    将第一部分和第二部分的结果相加,就是最后的结果了。
    精度要求方面,用int就可以了:two billion=20亿<2*1024*1024*1024=2^31,需用31位来表示数组,由于第一位总是1,所以求组合数的时候最多求30,C(30,k),k取值区间是[0,30],因为C(k,i)<2^k,所以结果用int表示就可以

 代码参考kuangbin的代码:

#include<cstdio>

int c[33][33];

void init(){
    c[0][0]=1;
    c[1][0]=1;
    c[1][1]=1;
    for(int i=2;i<33;i++){
        c[i][0]=1;
        for(int j=1;j<i;j++){
            c[i][j]=c[i-1][j-1]+c[i-1][j];
        }
        c[i][i]=1;
    }
}

int bits[33];

int calc(int n){
    if(n<=1) return 0;
    int len=0;
    while(n>0)
    {
        if(n&1) bits[len++]=1;
        else bits[len++]=0;
        n>>=1;
    }
    int ans=0;
    for(int i=len-1;i>0;i--){
        if(i%2==0){
            ans+=(1<<(i-1))>>1;
        }else{
            ans+=((1<<(i-1))-c[i-1][(i-1)>>1])>>1;
        }
    }
    int cnt0=0,cnt1=0;
    for(int i=0;i<len;i++){
        if(bits[i]==0) cnt0++;
        else cnt1++;
    }
    if(cnt0>=cnt1) ans++;
    cnt0=0;cnt1=1;
    for(int i=len-2;i>=0;i--){
        if(bits[i]==1){
            for(int j=i;j>=0&&j+cnt0+1>=i-j+cnt1;j--) ans+=c[i][j];
            cnt1++;
        }else cnt0++;
    }
    return ans;
}

int main(){
    init();
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF){
        printf("%d\n",calc(b)-calc(a-1));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40679299/article/details/82502460
今日推荐