题解:LG-P9589

题目链接:洛谷P9589

题外话:这题还是挺讲细节的。总体来说是一道水的绿题。洛谷那个审核一直在卡我格式。。。

题意重述

说白了就是选一个 \(a_i\) 的公约数 \(x\),不断地用 \(b_i\) \(a_i\) 直到所有的 \(a_i\) 都等于 \(x\)。(\(a\)\(b\) 表示 \(b\div a\)

思路分析

这题我们从答案(也可以说是朴素解法)入手会方便一些。

找答案

  1. 枚举:计算答案时枚举 \(a_i\) 的公约数。它们一定是 \(a_i\) 的最大公约数的约数!至于最大公约数(姑且记为 \(Gcd\)),可以使用 cmath 中的 __gcd(a,b) 来计算,而枚举因数也很简单,\(O(\sqrt{Gcd})\) 枚举 \([1,\sqrt{Gcd}]\) 以内 \(Gcd\) 的小因数,其他的可以通过小因数 \(Gcd\) 的方法计算出来。
  2. 计算:对于每个枚举到的因数 \(x\),我们枚举 \(a_i\),将 \(a_i\) 降到 \(x\) 的最小步数累加。这样,朴素的思路就想好啦!

想正解

  1. 优化:这时你会发现一个问题:时间太高了!所以我们要考虑优化。发现 \(a_i\) 用除法降到 \(x\) 可以转化成用用来除的那些数即 \(b_i\) 乘出 \(\frac{a_i}{x}\),而这个是会重复用的,可以预处理。然后计算答案部分复杂度就降成了 \(O(n\times\sqrt{Gcd})\)。事实上跑不满,\(Gcd\) 的约数就最多只有 \(200\) 个,这样最多就执行 \(200\times(5\times 10^5)=10^8\) 次,显然不会超时了。
  2. 预处理:我们预处理 \([1,Gcd]\) 内用 \(b_i\) 乘出每个数的最少步数,这一块很自由,你可以选择用 BFS 或其他的高效算法。BFS 时间复杂度为 \(O(Gcd\times \log{Gcd}+m)\),不会超时。当然,记得把 \(b\) 排序然后遇到超过 \(Gcd\) 的直接 break,不然你会像我一样喜提 TLE!

整理

我们先按 4. 里讲的进行预处理,然后按 1.2. 里说的计算答案。上面已经证明过不会超时,虽然可能卡线。时间再紧一点就要用结构体去重了,去重后一定不会超,学有余力的同学可以试试。

总结

本题对算法深度要求不高,但是需要想到转化、枚举和预处理思想,而且对于预处理不能只想到 DP,还要知道有时会用比较靠近图论的算法如 BFS,有的算法甚至是将代数转化为图论。

AC代码

#include<bits/stdc++.h>
#define N 500005
#define maxn 500000
#define SS 0x3f3f3f3f3f3f3f3f
#define ll long long
using namespace std;
ll n,m,a[N],b[N],f[N];
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    ll Gcd=0;
    for(int i=1;i<=n;i++){cin>>a[i];Gcd=__gcd(Gcd,a[i]);}
    for(int i=1;i<=m;i++)cin>>b[i];sort(b+1,b+m+1);m=unique(b+1,b+m+1)-b-1;
    memset(f,-1,sizeof(f));
    f[1]=0;
    //BFS O(maxn+m)
    queue<ll> q;q.push(1);
    while(!q.empty()){
        int h=q.front();q.pop();
        for(int i=1;i<=m&&b[i]*h<=maxn;i++){//这里把判断越界提到了前面,跟break是一个效果。
            if(f[b[i]*h]==-1){
                f[b[i]*h]=f[h]+1;
                q.push(b[i]*h);
            }
        }
    }
    ll p=Gcd;
    long long ans=SS;
    //O(np),p=Gcd<=max_a
    for(int i=1;1ll*i*i<=p;i++){//枚举因数(O_/p)
        if(p%i==0){
            //计算(On),但最多200个因数
            long long tmp=0;
            for(int k=1;k<=n;k++){
                if(f[a[k]/i]!=-1)tmp+=f[a[k]/i];
                else{
                    tmp=SS;break;
                }
            }
            ans=min(ans,tmp);
            if(i*i!=p){
                tmp=0;
                for(int k=1;k<=n;k++){
                    if(f[a[k]/p*i]!=-1)tmp+=f[a[k]/p*i];
                    else{
                        tmp=SS;break;
                    }
                }
                ans=min(ans,tmp);
            }
        }
    }
    if(ans==SS)cout<<-1;
    else cout<<ans;
    return 0;
}
posted @ 2026-05-13 20:48  zap2727  阅读(8)  评论(0)    收藏  举报