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]);
}
}