解题思路:
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分
3 人评分
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程
发表评论 取消回复