POJ Round Numbers 3252 (组合数学)

来源:互联网 发布:嵌入式linux视频 编辑:程序博客网 时间:2024/04/30 08:27

Round Numbers
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 12153 Accepted: 4606

Description

The cows, as you know, have no fingers or thumbs and thus are unable to play Scissors, Paper, Stone' (also known as 'Rock, Paper, Scissors', 'Ro, Sham, Bo', and a host of other names) in order to make arbitrary decisions such as who gets to be milked first. They can't even flip a coin because it's so hard to toss using hooves.

They have thus resorted to "round number" matching. The first cow picks an integer less than two billion. The second cow does the same. If the numbers are both "round numbers", the first cow wins,
otherwise the second cow wins.

A positive integer N is said to be a "round number" if the binary representation of N has as many or more zeroes than it has ones. For example, the integer 9, when written in binary form, is 1001. 1001 has two zeroes and two ones; thus, 9 is a round number. The integer 26 is 11010 in binary; since it has two zeroes and three ones, it is not a round number.

Obviously, it takes cows a while to convert numbers to binary, so the winner takes a while to determine. Bessie wants to cheat and thinks she can do that if she knows how many "round numbers" are in a given range.

Help her by writing a program that tells how many round numbers appear in the inclusive range given by the input (1 ≤ Start < Finish ≤ 2,000,000,000).

Input

Line 1: Two space-separated integers, respectively Start and Finish.

Output

Line 1: A single integer that is the count of round numbers in the inclusive range Start..Finish

Sample Input

2 12

Sample Output

6

Source

USACO 2006 November Silver


题意:定义一个十进制的数,把他转化为二进制的数,最高位为1,如果这个二进制的数中0的个数大于等于1的个数,就说这个数为整数,本题给出一个闭区间,让求这个区间的有多少个整数


分析:求n到m之间的整数,我们可以求小于等于m的整数的个数,还有小于等于(n-1)的整数的个数(因为包含n),

相减就得出结果

   1.如何求小于等于n的整数的个数

     先算小于n的二进制位数的整数的个数

     再加上等于n的二进制位数的整数的个数

     

     比如: 60=111100,它的二进制长度为6,我们先算二进制长度小于6的整数,这样的数肯定小于n

                  先知道这个公式 C(n,0) + C(n,1) +C(n,2) + ...... +C(n,n) = 2^n

     

                  长度为5的二进制整数 1  _  _  _  _  第一位必须是1,且0的个数大于等于1的个数

                  可以是4个0和1个1,这种情况的个数是 C(4,4)  (因为第一位必须为1,后面4位选4个0)

                  可以是3个0和2个1,这种情况的个数是 C(4,3) (因为第一位必须是1,后面4位选3个0)

                  其他的情况就不符合整数0的个数大于等于1的个数的性质了

     这种情况的和就是  C(4,4) + C(4,3)  =  ( 2^4 - C(4,2) ) / 2


                  长度为4的二进制的整数 1  _  _  _  第一位必须是1,且0的个数大于等于1的个数

     可以是 3个0和1个1,这种情况的个数就是 C(3,3)   (第一位必须是1)

                  也可以是2个0和2个1,这种情况的个数是  C(3,2)  (第一位必须是1)

     和就是 C(3,3)+C(3,2)=( 2^3 ) / 2


                  长度为3的二进制个数就是 C(2,2) = ( 2^2 - C(2,1) ) / 2

        

                  长度为2的二进制个数是 C(1,1) = ( 2^1 ) / 2


                  长度为1的二进制个数是 0


                   这就是小于等于n的整数的个数

      假设n的二进制长度为 L

      当L为奇数 二进制长度为L的整数的个数就是 ( 2^(L-1) - C(L,(l/2)) ) / 2

                   当L为偶数 二进制长度为L的整数的个数就是   ( 2^(L-1) ) / 2

                  这样就完成了第一步,求出来二进制长度小于等于n的二进制长度的整数个数

     

     

                    接下来,我们二进制长度等于L且小于n的整数的个数

       比如  1010,这个二进制数,第一位肯定没法改变,然后看第二位,是0,如果改变,就变成1,变成了

                    1100那这个数就比1010大了,不符合小于等于,所以也不能改变,那看第三位,1变成0,变成了1000

                    这种情况就符合,然后我们求这种情况的整数的个数,此时,1000,第三位前有1个1,2个0,所以第

       四位可以是0也可以不是0,就是 C(1,1) + C(1,0),然后把第三位还原,看第四位....这一步看代码


                                   

#include <stdio.h>#include <string.h>#include <stdlib.h>int c[35][35];void play_table()   //杨辉三角求组合数,打表{    int i,j;    memset(c,0,sizeof(c));    for(i=0;i<33;i++)    {        for(j=0;j<33;j++)        {            if(!j || i==j)            {                c[i][j]=1;            }            else            {                c[i][j]=c[i-1][j-1]+c[i-1][j];            }        }    }}int b[40];int f(int x){    if(x<=1)        return 0;    int len=0,ans=0;    int i,j;    while(x)    //转二进制    {        if(x&1)      //x%2==1,感觉位运算好高大上            b[len++]=1;        else            b[len++]=0;        x=x>>1;    }    for(i=len-1;i>0;i--) //求小于len长度的整数有多少,上面解释的第一大步    {        if(i&1)     //i是奇数 i-1就是偶数            ans+=((1<<(i-1))-c[i-1][(i-1)/2])/2;        else            ans+=(1<<(i-1))/2;    }    int ct0=0,ct1=0;    for(i=len-1;i>=0;i--)    {        if(b[i]==0)            ct0++;        else            ct1++;    }    if(ct0>=ct1)  //判断一下它自己本身是不是整数        ans++;    ct1=1;    ct0=0;    for(i=len-2;i>=0;i--)  //第高一位肯定为1从高第二位开始找,找到1,把它变成0,然后统计后面的,第二大步    {        if(b[i]==1)  //如果这以为是1,变为0,计算后面可能的情况        {            for(j=i;j>=0 && j+ct0+1>=i-j+ct1;j--)  //  j+ct0+1>=i-j+ct1,保证0的个数始终大于等于1的个数            {                ans+=c[i][j];               }            ct1++;  //统计1出现的次数        }        else        {            ct0++;   //统计0出现的次数        }    }    return ans;}int main(){    play_table();    int n,m;    scanf("%d%d",&n,&m);    printf("%d\n",f(m)-f(n-1));//n到m 包含n,所以求n-1小的整数    return 0;}



下面是自己一开始写的,答案对,但是超级超级超级超级慢,纯属娱乐

/*超一万倍的时间,但是答案对*/ #include <iostream>#include <stdio.h>#include <string.h>using namespace std;int getnumone(int x)//统计一个数的二进制1的个数{    int ct=0;    while(x)    {        ct++;        x=x&(x-1);    }    return ct;}int weishu(int x){    int ct=0;    while(x)    {        ct++;        x=x>>1;    }    return ct;}int f(int x,int y){    int i,j,a,b,c,d,ct=0;    for(i=x;i<=y;i++)    {        a=getnumone(i);        b=weishu(i);        //printf("%d   %d\n",a,b);        c=b-a;        if(a<=c)        {            ct++;        }    }    return ct;}int main(){    int n,m,i,j,x;    scanf("%d%d",&n,&m);    x=f(n,m);    printf("%d\n",x);    return 0;}




1 0
原创粉丝点击