SDUT 2021 Autumn Team Contest 25th

这篇博客详细解析了SDUT 2021秋季团队竞赛中的两道题目。D题ABC Conjecture探讨了关于质因子和rad函数的性质,提出当c大于rad(abc)时的条件。分析中提到了如何判断c是否满足条件,涉及到质数平方和平方根的计算。L题Clock Master涉及将n拆分为若干个数,要求这些数的最大最小公倍数最大化,解决方案利用了质数分组和背包策略,并通过log运算避免了数值溢出问题,预处理降低了时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写目录标题

D - ABC Conjecture

题意:
给定一个 c ( [1 , 1e18] ),问 c 是否满足以下性质:
①:将 c 分成 a 和 b,a + b = c
②:c > rad(abc)
注:rad(n) 为 n 的所有质因子相乘
分析:
注意:举了几个样例,发现 rad(abc) 和 rad( c ) 的差别很小,因此不考虑 a 和 b,只考虑 c,具体怎么证不会,以下只是对 rad 性质的讨论。
由 rad 的定义可知,如果 n 的所有因子(不包括 1 和 n)都是质因子,那么 rad(n) = n,例如 rad(6) = 2 x 3 = 6,rad(105) = 3 x 5 x 7 = 105,而 rad(18) = 2 x 3 = 6,因为 18 的质因子只有 2 和 3,因子却有 2,3,6,9。
可以看出来,将 n 分解成质因子的幂次方相乘的形式,如果没有一个质因子的幂次方 > 1 那么 n 的所有因子都是质因子,所以 rad(n) = n,所以必然是 no。
接下来讨论输出yes的情况:(以下 n 全部为 1e18 左右的数)
如果 n 的质因子只有两个:想要输出yes,n 只能是平方数,即这两个质因子相同
如果 n 的质因子超过两个:想要输出yes,必然要有找到某个质因子的平方能被 n 取模,所以会出现以下几种情况
① n 质因子全是1 ~ 1e6 范围内的,只需要将 1e6 之前的素数全部预处理出来,让 n 去取模素数平方就行了,只要有一个符合就可以输出 yes
② n 的质因子由 1e6 以上的质数部分1 ~ 1e6 内的幂都是 1 的质数组成,那么想要输出 yes,n 只能由 1e6 以上质数的平方组成(只能是平方,不能是三次方及以上,因为这样就超过1e18了),所以只要把 1 ~ 1e6 以内的质因子都除掉,然后判读是否为平方数即可
步骤:
第一步:预处理出 1 ~ 1e6 内的所有素数
第二步:输入 c,然后判断 c 是否能取模 1 ~ 1e6 内的素数的平方,如果能,直接输出yes,如果不行,执行下一步
第三步:将 1e6 内的质因子全部除掉,一定要先判断此时 n 是否是 1,然后判断 c 是否是平方数,是则 yes,否则 no。

#include <iostream>
#include <cmath> 
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;

int cnt;
LL pr[N];
bool st[N];

void get_Prime()
{
	for(int i = 2; i <= N; i ++ )
	{
		if(!st[i]) pr[cnt ++ ] = i;
		for(int j = 0; pr[j] <= N / i; j ++ )
		{
			st[pr[j] * i] = true;
			if(i % pr[j] == 0) break;
		}
	}
}

int main()
{
	get_Prime();
	
	int t;
	cin >> t;
	while(t -- )
	{
		LL n, flag = 0;
		cin >> n;
		
		for(int i = 0; i < cnt; i ++ ) if(n % (pr[i] * pr[i]) == 0) flag = 1;
		
		if(flag == 1)
		{
			cout << "yes\n";
			continue;
		}
		
		for(int i = 0; i < cnt; i ++ ) if(n % pr[i] == 0) n /= pr[i];
		
		if(n == 1)
		{
			cout << "no\n";
			continue;
		}
		
		LL m = sqrt(n);
		
		if(m * m == n) cout << "yes\n";
		else cout << "no\n";
	}
}

L - Clock Master

题意:
给定一个 n,将 n 分成若干个数相加,然后求这些数的最大的最小公倍数
分析:
想要得到最大的最小公倍数,只要让这些数全部互质即可,也就是将前 n 之前的所有质数都找出,然后将每一个质数和它的幂都分到一组,用分组背包找到最优解
思路是这样,但是实现起来并不简单,因为 n 的最大值是30000,将30000进行上面操作会爆longlong的,所以题目中让用 log 以 e 为底的形式输出,这也是进一步给了我们一个提示,输出的是 log(dp[n]),dp数组因为乘法运算爆longlong,而乘法在log中会变成加法,所以dp数组存的数是已经log之后的,这样就可以解决爆longlong的问题
但是麻烦并不止这一个,30000之前的素数个数是有3245个,强行写分组背包最差时间是 3245 x 30000 = 1e8,时间非常紧迫,所以要把能预处理的全部预处理出来
要注意的是,log运算的时间很高,我没预处理log运行,编译器显示时间是1.7s左右,但是预处理log后就直接把时间缩到1s以内

#include <iostream>
#include <math.h>
using namespace std;
typedef long long LL;
const int N = 3e4 + 10;

int cnt = 0;
LL pr[N][20];
bool st[N];
double dp[N];
double lg[N];

void get_Prime()
{
	for(int i = 2; i <= N; i ++ )
	{
		if(!st[i]) pr[cnt ++ ][1] = i;
		for(int j = 0; pr[j][1] <= N / i; j ++ )
		{
			st[pr[j][1] * i] = true;
			if(i % pr[j][1] == 0) break;
		}
	}
	for(int i = 0; i < cnt; i ++ )
	{
		for(int j = 2; pr[i][j - 1] < N; j ++ )
		{
			pr[i][j] = pr[i][j - 1] * pr[i][1];
		}
	}
}

void get_Log()
{
	for(int i = 1; i < N; i ++ ) lg[i] = log(i);
}

void get_Dp()
{
	for(int i = 0; i < cnt; i ++ )
	{
		for(int j = 30000; j >= 0; j -- )
		{
			for(int k = 1; pr[i][k] <= 30000; k ++ )
			{
				if(j >= pr[i][k])
				{
					dp[j] = max(dp[j], dp[j - pr[i][k]] + lg[pr[i][k]]);
				}
			}
		}
	}
}

int main()
{
	get_Prime();
	get_Log();
	get_Dp();
	
	int t;
	cin >> t;
	while(t -- )
	{
		int m;
		cin >> m;
		printf("%.9lf\n", dp[m]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值