题目大意
给出一个序列 a a a,求出 g c d ( { l c m ( a i , a j ) ∣ i < j } ) gcd(\{lcm(a_i,a_j) | i<j\}) gcd({lcm(ai,aj)∣i<j})。
做法一
实际上不难发现序列中 a 1 a_1 a1对应的 l c m lcm lcm对为 n − 1 n-1 n−1对, a 2 a_2 a2对应的 n − 2 n-2 n−2对…以此类推,关键是如何化简如下的表达式:
g c d { l c m ( a 1 , a 2 ) , l c m ( a 1 , a 3 ) , . . . , l c m ( a 1 , a n ) } gcd \{ lcm(a_1,a_2),lcm(a_1,a_3),...,lcm(a_1,a_n) \} gcd{lcm(a1,a2),lcm(a1,a3),...,lcm(a1,an)}
GCD和LCM在质因数分解下的意义
假设 a a a经过质因数分解后为 P 1 k 1 ∗ P 2 k 2 , . . . ∗ P n k n P_1^{k_1}*P_2^{k_2},...*P_n^{k_n} P1k1∗P2k2,...∗Pnkn, b b b经过质因数分解为 Q 1 t 1 ∗ Q 2 t 2 , . . . ∗ Q n t n Q_1^{t_1}*Q_2^{t_2},...*Q_n^{t_n} Q1t1∗Q2t2,...∗Qntn
假设质因子 p p p为 a a a和 b b b共有的质因子,在 a a a中 p p p的幂次为 k 1 k_1 k1,在 b b b中 p p p的幂次为 k 2 k_2 k2,又因为所有的质因子两两互质,互不影响
那么 g c d ( a , b ) gcd(a,b) gcd(a,b)的质因子 p p p实际上的幂次为 m i n { k 1 , k 2 } min\{ k_1,k_2 \} min{k1,k2}, l c m ( a , b ) lcm(a,b) lcm(a,b)的质因子 p p p实际上的幂次为 m a x { k 1 , k 2 } max\{ k_1,k_2 \} max{k1,k2}
那么现在我们再看上式,对于所有数的一个公共的质因子 p p p,假设幂次分别为 k 1 , k 2 , . . . k n k_1,k_2,...k_n k1,k2,...kn,上式的意义为:
m i n { m a x ( k 1 , k 2 ) , m a x ( k 1 , k 3 ) , . . . , m a x ( k 1 , k n ) } min \{ max(k_1,k_2),max(k_1,k_3),...,max(k_1,k_n) \} min{max(k1,k2),max(k1,k3),...,max(k1,kn)}
然后该式等价于:
m a x { k 1 , m i n { k 2 , k 3 , . . . , k n ) } } max \{ k_1,min \{k_2,k_3,...,k_n) \} \} max{k1,min{k2,k3,...,kn)}}
即化为表达式:
l c m { k 1 , g c d { k 2 , k 3 , . . . , k n } } lcm \{ k_1,gcd \{ k_2,k_3,...,k_n \} \} lcm{k1,gcd{k2,k3,...,kn}}
因此我们得到等式:
g c d { l c m ( a 1 , a 2 ) , l c m ( a 1 , a 3 ) , . . . , l c m ( a 1 , a n ) } = l c m { k 1 , g c d { k 2 , k 3 , . . . , k n } } gcd \{ lcm(a_1,a_2),lcm(a_1,a_3),...,lcm(a_1,a_n) \} = lcm \{ k_1,gcd \{ k_2,k_3,...,k_n \} \} gcd{lcm(a1,a2),lcm(a1,a3),...,lcm(a1,an)}=lcm{k1,gcd{k2,k3,...,kn}}
其它收获
得知上述等式后,我们对左边和右边
l
c
m
lcm
lcm化
g
c
d
gcd
gcd得到:
然后两边约去
a
1
a_1
a1:
对于左式的分母,等价于
g
c
d
{
a
1
,
g
c
d
{
a
2
,
a
3
,
.
.
.
,
a
n
}
}
gcd\{ a_1,gcd\{ a_2,a_3,...,a_n \} \}
gcd{a1,gcd{a2,a3,...,an}},那么我们将
a
1
a_1
a1看做
x
x
x,将
g
c
d
{
a
2
,
a
3
,
.
.
.
,
a
n
}
gcd\{ a_2,a_3,...,a_n \}
gcd{a2,a3,...,an}看作
y
y
y,那么左式实际上变成了:
y / g c d ( x , y ) y/gcd(x,y) y/gcd(x,y)
那么,实际意义就是将 y y y和 x x x相同的最大公因数从 y y y中约去,而这时再看右式,不难发现实际就是将 y y y中的每一部分与 x x x相同的最大公因数约去,又因为 g c d gcd gcd运算和乘法的交换性(即 k ∗ g c d ( a , b ) = g c d ( k ∗ a , k ∗ b ) k*gcd(a,b)=gcd(k*a,k*b) k∗gcd(a,b)=gcd(k∗a,k∗b))最后再求 g c d gcd gcd即约去了整体与 x x x的最大公因数
最后对于本题,我们只需预处理后缀的 g c d gcd gcd即可
PS:本题的公式还可用另外方法证明,见mrcrack的博客
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;
int a[maxn],sub[maxn];
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sub[n]=a[n];
for(int i=n-1;i>=1;i--)
sub[i]=gcd(sub[i+1],a[i]);
ll ans=0;
for(int i=1;i<=n;i++)
ans=gcd(ans,lcm(a[i],sub[i+1]));
cout<<ans<<endl;
return 0;
}
做法二
根据上述在质因数分解意义下的 g c d gcd gcd和 l c m lcm lcm,实际上就是需要求出 n n n个数中同时出现最少 n − 1 n-1 n−1次的质因子中幂次最小和次小的那个,具体做法要出现 n n n 次和 n − 1 n-1 n−1 次来定。
//
// Created by Happig on 2021/1/24.
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;
unordered_map<int, int> fi, se;
unordered_set<int> st;
int minp[maxn], prime[maxn], vis[maxn];
int cnt;
void euler() {
for (int i = 1; i < maxn; i++) minp[i] = i;
for (int i = 2; i < maxn; i++) {
if (minp[i] == i) {
prime[cnt++] = i;
}
for (int j = 0; j < cnt && i * prime[j] < maxn; j++) {
minp[i * prime[j]] = prime[j];
if (i % prime[j] == 0) break;
}
}
}
void divide(int n) {
map<int, int> mp;
int fac, p, num;
while (n > 1) {
fac = minp[n];
n /= fac;
mp[fac]++;
}
for (auto i:mp) {
p = i.first, num = i.second;
vis[p]++, st.insert(p);
if (!fi.count(p)) {
fi[p] = num;
continue;
}
if (!se.count(p)) {
if (num < fi[p]) {
se[p] = fi[p];
fi[p] = num;
} else se[p] = num;
continue;
}
if (num <= fi[p]) {
se[p] = fi[p];
fi[p] = num;
} else if (num < se[p]) {
se[p] = num;
}
}
}
ll qkp(ll x, ll n) {
ll ans = 1;
while (n) {
if (n & 1) ans = ans * x;
x *= x;
n >>= 1;
}
return ans;
}
int main() {
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
euler();
for (int i = 1, x; i <= n; i++) {
cin >> x;
divide(x);
}
ll ans = 1;
for (auto p:st) {
//cout << p << ": " << fi[p] << " " << se[p] << ENDL;
if (vis[p] >= n - 1) {
if (vis[p] == n - 1) ans *= qkp(p, fi[p]);
else ans *= qkp(p, se[p]);
}
}
cout << ans << ENDL;
return 0;
}