Orac and LCM CodeForces - 1349A
Orac and LCM
方法一:
先说结论:
- gcd(a, lcm(b, c)) = lcm(gcd(a, b), gcd(a, c))
- lcm(a, gcd(b, c)) = gcd(lcm(a, b), lcm(a, c))
这就是传说中的分配律嘛,爱了爱了。我们知道答案就是要求__gcd(lcm(a1,a2), lcm(a1,a3)......,lcm(a2,a3), lcm(a2,a4)......lcm(an-1,an)),所以我们不妨把其分解成__gcd(__gcd(lcm(a1,a2), lcm(a1,a3)......), __gcd(lcm(a2,a3), lcm(a2, a4),.......)。
对于__gcd(lcm(a1, a2), lcm(a1, a3), ......)由结论可推导原式等于__gcd(a1, lcm(a2, a3, ......an))
对于__gcd(lcm(a2, a3), lcm(a2, a4), ......)由结论可推导原式等于__gcd(a2, lcm(a3, a4, ......an))
故答案为:__gcd(__gcd(a1, lcm(a2, a3, ......an)), __gcd(a2, lcm(a3, a4, ......an)), ...... __gcd(an-1, lcm(an))
我们可以通过一个后缀数组进行求解lcm。
代码见:https://blog.csdn.net/weixin_44116061/article/details/106348618
方法二:
题解中说,若要满足某个质数p的k次方对于最后答案有影响,那么其一定是至少在数组有n-1个a分解成质数后均含有p才能,试想,如果n-1个都含有p,那么任意的lcm都会含有一个含有p的a,那么所有的lcm结果均包含p,最终的结果也会包含p的k次方(其中k为n-1个a里面最小的指数幂,因为要求最大公约数,必定是取最小的因为取最小所有的都满足整除p的k次)。如果全含有p,那么也是一样的,只不过这里是要第二小(我们知道lcm(p^1, p^2)肯定是p^2,所以所有的lcm结果都是两个中p的指数大的那个,然后再取这些的最小,那么最小的指数在lcm中就被淘汰了,在剩下的选最小的k只能是第二小的了)。对于某个数,如果我们忽略掉,那么剩余数的gcd就是n-1个a分解后均含有的p的k乘积(p1^k1*p2^k2......),但是不同数忽略之后的gcd可能有p是相同的,那么我们要取唯一的p,那么我们采用对于每个gcd求所有的gcd的lcm即可。对于去掉某个a求剩余的gcd,我们可以采用分别求前缀的gcd和后缀的gcd再一次gcd得到。
可行性简单描述:
因为对于所有的质数p,如果它是在n-1个中出现,那么它必定在某个a被去掉的gcd中出现;如果在n个中均出现,对于求所有的gcd的lcm而言,在含有p的最小k的a被去掉后剩余的gcd中是包含了p的第二小的k的幂的,其余的情况因为p的最小的k没被去掉,所以剩余的gcd中包含的都是p的最小k的幂,lcm的时候对于p是取到的是第二小的,证明见上面证明lcm(p^1, p^2)结果是p^2的那里。而我们知道如果在n个中出现,正确的结果就是p的第二小的幂;对于在小于n-1个中出现的,在去掉任意一个元素所求的gcd中均不会出现该元素。综上,采用对于分别去掉某个a对于剩下的采用gcd,然后对所有的gcd进行lcm的结果是正确的。
AC代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll ; const int maxn = 100005; ll pre[maxn]; ll suf[maxn]; ll a[maxn]; ll ans; int main() { int n; scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%lld", &a[i]); pre[0] = a[0], suf[n - 1] = a[n - 1]; for(int i = 1; i < n; i++) { pre[i] = __gcd(pre[i - 1], a[i]); } for(int i = n - 2; i >= 0; i--) { suf[i] = __gcd(suf[i + 1], a[i]); } for(int i = 0; i < n; i++) { if(i == 0) ans = suf[1]; else if(i == n - 1) ans = ans / __gcd(ans, pre[n - 2]) * pre[n - 2]; else ans = ans / __gcd(ans, __gcd(pre[i - 1], suf[i + 1])) * __gcd(pre[i - 1], suf[i + 1]); } printf("%lld\n", ans); return 0; }
其实建议第一种方法。