Codeforces Round #641 (Div. 2) C. Orac and LCM(gcd和lcm的重要理解)

传送门


题目大意

给出一个序列 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 n1对, a 2 a_2 a2对应的 n − 2 n-2 n2对…以此类推,关键是如何化简如下的表达式:

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} P1k1P2k2,...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} Q1t1Q2t2,...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{k1min{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{k1gcd{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{k1gcd{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) kgcd(a,b)=gcd(ka,kb))最后再求 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 n1次的质因子中幂次最小和次小的那个,具体做法要出现 n n n 次和 n − 1 n-1 n1 次来定。

//
// 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值