题目链接:https://vjudge.net/problem/POJ-3252
知识点:组合数的一个递推式:C(i,j)=C(i-1,j-1)+C(i-1,j) (易证)
解题思路:
要求有多少个round number小于或等于某一个数S (其二进制位数为t),我们分三类进行:
1、看看S本身是不是round number。对于该类,只需将S转换成二进制并记录其0、1个数即可验证;
2、找出二进制位数小于 t 的所有round number。我们给出一个int cul (int n)函数来求二进制长度为n的所有round num,首先需要知道的是:其第一位必定为1。我们要保证0的个数多于或等于1的个数,则我们可以得出如下公式:若n为奇数,则cul(n) = C(n-1,n/2+1) + C(n-1,n/2+2) + ... + C(n-1,n-1); 若n为偶数,则cul(n) = C(n-1,n/2) + C(n-1,n/2+1) + ... + C(n-1,n-1), 另外,由于当n=1,我们会求到 C(0,1),这显然不妥,于是我们在函数中加上条件:if(n<=1) return 0; 。则该类round number的总数为: cul(t-1) + cul(t-2) + ... + cul(1)。
3、现在需要找出二进制位数等于 t 的 round number。我们用 cnt1 和 cnt0 两个变量记录S中0和1个数的实时变化。首先,每个数的第一个数都是1,则初始化cnt1=1,cnt0=0.接下来我们遍历给出的数的后面的每一位,如果该位上的数为0,则只需cnt0++即可,因为我们能且只能在该位上放置0;如果该位上的数是1,则我们可以先将该位数看为0,"暂时" (注意,只是暂时的,等下还要把cnt0恢复回去)地把cnt0看为 cnt0+1 ,那么后面的数字肯定是无论我们怎么取值,都肯定小于S,这就有点像第2类,求二进制位数小于某个数的所有round number, 但是在这里还需要考虑 cnt1 和 cnt0 需要保证0的总个数大于或等于1的总个数,我们设0的个数为 j ,后面的二进制位数为 i ,则在此处的round number数为:C(i-1,i-1) + C(i-1,i-2) + ... + C(i-1,j) . 需要保证 j + cnt0 >= i - j + cnt1,这里的cnt0其实就是cnt0 + 1,详情请参考代码。最后别忘了cnt1++。
AC代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 int C[35][35]; 5 void init(){ 6 C[0][0]=1; 7 C[1][1]=C[1][0]=1; 8 for(int n=2;n<35;n++){ 9 C[n][0]=C[n][n]=1; 10 for(int m=1;m<n;m++) 11 C[n][m]=C[n-1][m-1]+C[n-1][m]; 12 } 13 } 14 int cul(int n){ 15 if(n<=1) return 0; 16 int s,ans=0; 17 if(n%2) s=n/2+1; 18 else s=n/2; 19 for(int i=s;i<n;i++) 20 ans+=C[n-1][i]; 21 return ans; 22 } 23 int finds(int num){ 24 if(num<=1) return 0; 25 int tmp=num,dig[50]; 26 int len,lastone; 27 int c1=1,c0=0; 28 for(len=0;tmp>0;tmp=tmp>>1,len++){ 29 if(len>0){ 30 if((tmp<<1)<lastone){ 31 dig[len]=1; 32 c1++; 33 } 34 else{ 35 dig[len]=0; 36 c0++; 37 } 38 } 39 lastone=tmp; 40 } 41 dig[len]=1; 42 int ans=0; 43 if(c1<=c0) ans++; 44 45 for(int i=1;i<=len-1;i++) 46 ans+=cul(i); 47 48 int cnt0=0,cnt1=1; 49 for(int i=len-1;i>0;i--){ 50 if(dig[i]==0) cnt0++; 51 else{ 52 for(int j=i-1;j>=0;j--){ 53 if(2*j<i+cnt1-2-cnt0) break; 54 ans+=C[i-1][j]; 55 } 56 cnt1++; 57 } 58 } 59 return ans; 60 } 61 int main() 62 { 63 init(); 64 int s,f; 65 scanf("%d%d",&s,&f); 66 printf("%d\n",finds(f)-finds(s-1)); 67 return 0; 68 }