poj 3252 组合数学,数论

Round Numbers
Time Limit: 2000MSMemory Limit: 65536K
Total Submissions: 7584Accepted: 2606

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

 
 
 
 
这个题目我觉得真的是不简单,唉,自己太水了,这个题目是求求【start,finsh】 之间的数符合二进制中0的数字大于1的数,主要是用了组合数学的思想,一开始我在做的时候是用传说中最快的方法求出一个数中1的个数,再求出
0的个数就是了,但是还是TLE,我顿时泪流满面。
 
 
组合数学总是可以做出来的嘛!!!
 
求start-finsh,的就是求出[0,start],[0,finsh],再相减就是了,但是因为包括start,所以要求出【0,start-1】,就是了,那么应该怎么求,举例来说,对于一个数的二进制形式,10010010,一个数的位数比他小,就肯定比他小了,那么对于一个位数是奇数的,即len=2*k+1 ,它的求法是 R(len)=1/2*{2^(2k)-C(2k,k)};而一个偶数为len=2*k,他的求法是R(len)=1/2*(2^(2k-1));为啥是这样呢,其中用到了二项式定理,
 
由于 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));
 
这里有一种高明的办法,是用C【】【】,的二维数组来表示出排列,如c[3][2],就是求出3位中挑出2位放0的情况,
而且用c[m][n]=c[m-1][n-1]+c[m-1][n],来求出每一种的排列数,实在高明!!!
 
那么对于位数于10010010,相同的怎么办的?
 
第一部分已经将长度小于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,
结果相加(由于前缀不一样,所以后面不管怎么组合都是唯一的)
 

实在是非常的聪明,我们用k+n0+1>=i-k+n1,来保证我们放进去0的个数加上前面的0的个数总体是大于1的位数的,
我没想出这个式子,看着别人的,水平实在不行啊!!!!
 
#include<iostream>
#include<cstdio>
#include<cstring>
#define M 31
using namespace std;
int c[M][M];
int pow[M];
int array[M];
int solve(int n)
{
     if(n<=1)
     return 0;
     int n0,n1,len;
     int i,j,k,t;
     int sum=0;
     for(i=0;i<M;i++)
     {
         if(pow[i]&n)
         array[i]=1;
         else
         array[i]=0;
     }
      /*for(i=0; i<M; ++i)
      array[i]=((pow[i]&n)!=0)?1:0;
     /* for(i=0;i<4;i++)
     {
         printf("ggg%d\n",array[i]);
     }*/
     for(i=M-1;i>=0;i--)
     {
          //printf("%d fdf %d\n",array[i],i);
         if(array[i]==1)
         {
             len=i;
             break;
         }
     }
     //printf("%d len %d\n",len,n);实际长度为len+1;
     for(i=len;i>=1;i--)     //R(len)=1/2*{2^(2k)-C(2k,k)};
     {                       //1/2*(2^(2k-1));
         if(i%2==1)
         {
             sum=sum+(pow[i-1]-c[i-1][(i-1)/2])/2;
         }
         else
         {
             sum=sum+(pow[i-1])/2;
         }
     }
     n0=0;
     n1=0;
     for(i=len;i>=0;i--)
     {
         if(array[i])
         n1++;
         else
         n0++;
     }
     if(n0>=n1)
     {
         sum++;
     }
     n0=0;
     n1=1;
     for(i=len-1;i>=0;i--)
     {
         if(array[i])
         {
           for(k=i;k>=0&&k+n0+1>=i-k+n1;k--)
           {
             sum=sum+c[i][k];
           }
           n1++;
         }
         else
         n0++;

     }
    return sum;
}
int main()
{
    int i,j,k;
    int start,finsh;
    for(i=0;i<M;i++)
    {
        c[i][0]=1;
        c[i][i]=1;
        pow[i]=(1<<i);
    }
    for(i=2;i<M;i++)
    {
        for(j=1;j<i;j++)
        {
            c[i][j]=c[i-1][j-1]+c[i-1][j];
        }
    }
    //for(i=0;i<M;i++)
    //printf("%d\n",pow[i]);
    scanf("%d%d",&start,&finsh);
    printf("%d\n",solve(finsh)-solve(start-1));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值