URAL - 1748 The Most Complex Number(数论+搜索)

URAL - 1748点我点我


Description

Let us define a complexity of an integer as the number of its divisors. Your task is to find the most complex integer in range from 1 to n. If there are many such integers, you should find the minimal one.

Input

The first line contains the number of testcases t (1 ≤ t ≤ 100). The i-th of the following t lines contains one integer ni (1 ≤ ni ≤ 10 18) .

Output

For each testcase output the answer on a separate line. The i-th line should contain the most complex integer in range from 1 to ni and its complexity, separated with space.

Sample Input

inputoutput
5
1
10
100
1000
10000
1 1
6 4
60 12
840 32
7560 64

题意 :给你一个n,让你找出小于等于n的数中因子个数最多的那个数,并且输出因子个数,如果有多个答案,输出数最小的那个

思路 : 官方题解 : 

(1)此题最容易想到的是穷举,但是肯定超时。

(2)我们可以知道,计算约数的个数和质因数分解有着很大的联系: 若Q的质因数分解为:Q=p1^k1*p2^k2*…*pm^km(p1…pm为素数,k1…km≥1),则Q有(k1+1)(k2+1)…(km+1)个约数。但是质因数分解的时间复杂度很高,所以也会超时。

(3)通过以上的公式,我们可以“突发奇想”:为何不能把质因数分解的过程反过来呢? 这个算法就是枚举每一个素数。初始时让m=1,然后从最小的素数2开始枚举,枚举因子中包含0个2、1个2、2个2…k个2,直至m*2^k大于区间的上限N。在这个基础上枚举3、5、7……的情况,算出现在已经得到的m的约数个数,同时与原有的记录进行比较和替换。直至所有的情况都被判定过了。 这个算法的优化:如果p1*p2*p3*……*pk>N(pi表示第i个素数),那么只要枚举到p k-1,既不浪费时间,也不会遗漏。

(4)以上的算法还不是最好的,还可以继续优化。 我们看以下的例子: 6=2*3 10=2*5 6和10的质因数分解“模式”完全相同,所以它们的约数个数是相同的。但是由于3<5,所以6<10。 12=2^2*3 18=3^2*2 12和18的质因数分解“模式”完全相同,所以它们的约数个数是相同的。但是由于12的质因数分解中2的指数大于3的指数,18的质因数分解中3的指数大于2的指数,所以12<18。 根据以上的举例,我们也可以对(3)中的算法进行一个改进:可以在枚举时进行一个优化,使得枚举到的数字中2的指数不小于3的指数,3的指数不小于5的指数……这样我们就能够得到质因数分解“模式”相同的最小数(证明略)。再对于每一个得到的数进行比较和记录。这个算法的优化力度极大,效率几乎达到了极限。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

#define LL long long 

int prime[16] = {1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47};
LL n, ans, ans_num;

void dfs(LL num, LL now, LL cnt, int maxys){
	if(now >= 16) return;
	
	if(cnt > ans || (cnt == ans && ans_num > num)){
		ans = cnt; ans_num = num;
	}
	
	LL p = prime[now];
	for(int i = 1; i <= maxys; i++){
		//if(num*p > n) break; 不能这么写,会溢出! 
		if(p > n/num) break;
		
		dfs(num*p, now+1, cnt*(i+1), i);
		p *= prime[now];
	}
}

int main(){
	freopen("test.in", "r", stdin);
	freopen("test.out", "w", stdout);
	
	int T;
	scanf("%d", &T);
	
	while(T--){
		scanf("%lld", &n);
		
		ans = ans_num = 1LL;
		dfs(1, 1, 1, 50);
		
		printf("%lld %lld\n", ans_num, ans);
	}
	
	return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值