牛客网练习赛14 — n的约数

题目:

题目描述 

t次询问,每次给你一个数n,求在[1,n]内约数个数最多的数的约数个数


1 <= n <= 1000000000000000000(18个0)


参考了大佬的代码,自己理解的,还有一部分还没有搞明白

首先,每一个正整数都能通过互不相同的素数的次方的积得出,下面是一条公式

设n=p1^k1*p2^k2*……*pn^kn,其中p1,p2,……,pn为互不相同的质数,k1,k2,……,kn为正整数;

所谓的约数就是能被n整除的正整数,我们把其中p1^k1移出来,显然p1^k1能被n整除,同理的p1^0,p1^1,p1^2一直到p1^k1都能被n整除,那么单单就p1^k1来说,能被n整除的数就有(k1+1),这个+1是包含p1^0的情况,以此类推,n的全部约数就有   (k1+1)*(k2+2)*……*(kn+1)个,这个结论很重要;

接下来只要枚举小于或者等于n的所有 p1^k1*p2^k2*……*pn^kn的组合,然后进行比较 (k1+1)*(k2+2)*……*(kn+1)的大小,选取最大的那个,就是答案;

下面是参考的是大佬的代码:代码下面对代码稍作一下解析(自己的理解)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int p[20] = { 0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47 }, T;
ll n;
int dfs(ll num, int pos, int k, int ans) { //num为当前的数值,pos为当前素数的位置,k为当前素数的个数,ans为当前因子的个数
	if (pos>15) return 0;
	int ret = ans;
	for (int i = 1; i <= k; ++i) {
		if (n / p[pos]<num) break;//防溢出
		num *= p[pos];

		ret = max(ret, dfs(num, pos + 1, i, ans*(i + 1)));
	}
	return ret;
}
int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%lld", &n);
		printf("%d\n", dfs(1, 1, 64, 1));
	}
	return 0;
} 

自己的理解:代码首先先列出了18个素数,在参考了其他大佬的代码,发现很多都是列大于或等于16个的素数就行了,这里我不太明白,感觉不是很够用,这些列举的素数是用来枚举上面所说的组合的,这里用到了dfs来进行计算和比较大小,具体过程是这样的:

    给定一个限制的素数最大次方数  64 (有些大佬的ac代码用了111)

    起始的数字num=1,使用的素数为事先列举的素数数组p中pos=1的素数,然后是一个限制的素数最大次方 k = 64 ,起始约数ans=1; 

  这里有一点要注意的是,dfs的最终结束条件是  for循环里面的 n/p[pos]<num  ,写成 n < p[pos]*num 会比较好理解一点,就是当前的数字num乘一个素数p[pos] 如果大于n就直接退出结束,但是为了防止数据太大溢出,所有才写成n < p[pos]*num ;

代码的dfs依次是

第一种组合是 p[1]*p[2]*...*p[15], 所有素数的次方都为1。

第二种是 p[1]^2*p[2]^2*...  次方数最高的是最小的那个素数,后面的素数的次方可能为2也可能为1. 每进入一层dfs就用ret把当前约数个数记录下来,然后继续把ans*(i+1)(就是最开始讲的(k1+1)*(k2+1)*...) 带入到下一层dfs中,同时把累乘的数字num和次方限制数  i  带入下一层,进行到最后一层结束dfs的时候不断的返回上一层,然后用max来判断当前这一层ret与下一层的 约数个数的最大值,返回这个最大值然后继续返回上上层;最终能得出所有小于或者等于n的 p1^k1*p2^k2*……*pn^kn的组合的约数最大值是多少;

在这里,我还有几点没有弄明白,希望有知道的大佬讲解一下(十分感谢)

1,代码中列举的素数为19个素数,而且最大的素数是47,在n 为19位数的情况下,真的够用吗?

2,为什么dfs中  pos>15  就返回0,不太明白这个条件中15的意思,有一些大佬的ac代码用的是16;

3,在起始的限制次方数k的值为什么是64,有些ac代码用的是111;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值