解题思路:

1 这个【评测用例规模与约定】格式有问题,N的上限基本上是long long的上限,所以这题N和结果都要用long long类型。

对于 30% 的评测用例,1 ≤ N ≤ 106, 1 ≤ K ≤ 10。
对于 60% 的评测用例,1 ≤ N ≤ 2 × 109, 1 ≤ K ≤ 30。
对于所有评测用例,1 ≤ N ≤ 1018, 1 ≤ K ≤ 50。

2 求含有k个1的个数,就是N的二进制表示,从1开始数,有多少个含k个1的组合数。但是从1开始循环到N,肯定超时,应该用Cn m的组合公式去算。

3 先从long long的8个字节最高位开始,向右依次判断是否为1,找到N的最高位,此时最高位1后面全都是0的那个2的i次记为t。最后的结果就可以=小于t的i-1个1的全组合个数Ci-1,k  +  N-t之后的差,继续向右寻找最高位,继续以上过程所得的所有组合数。这是一个可以递归的过程。

4 组合数公式计算用到3个数的阶乘,n! m!  (n-m)!,按照普通方法去计算,long long也会溢出,所以需要计算时一边乘一边除,尽早约掉因子。

5 递归的结束条件,不止一个吧,f( i ,1)的话就直接返回i 。


参考代码:

#include <stdio.h>

unsigned  long long cnm(int n,int m)
{
    long long s = 1;
    int k = 1;
    if(m > n/2)
        m = n-m;
    for(int i=n-m+1;i<=n;i++)
    {
        s *= (long long)i;
        while(k<=m && s%k == 0)
        {
            s /= (long long)k;
            k++;
        }
    }
    return s;
}

unsigned long long f(unsigned long long n,int k)
{
    unsigned long long c1=0,c2=0,c=0,t=0x8000000000000000;
    int i=64;
    while(~n&t){
        t=t>>1;
        i--;
    }
    if(k==1) return i;
    if((i-1)<k) return 0;
    if(n&t){
        c1=cnm(i-1,k);
        if(k>1){
            c2=f(n-t,k-1) ;
            c=c1+c2;
        }
        else
            c=c1;
    }
    return c;
}

int main()
{
    unsigned long long n, y,c=0,m;
    int k;
    scanf("%lld%d",&n,&k);
    c=f(n,k);
    printf("%lld",c);
}


点赞(0)
 

0.0分

3 人评分

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 0 条评论

暂无评论