题解 洛谷 P3532 【[POI2012]ODL-Distance】

\(cnt(x)\)\(x\) 质因数分解后质因数的指数和,即将 \(x\) 不断除其一个约数来使其变为 \(1\) 所需的次数,其可以通过线性筛来预处理。

不难发现:

\[\large d(a_i,a_j)=cnt(a_i)+cnt(a_j)-2cnt(\gcd(a_i,a_j)) \]

考虑对于每个 \(a_i\),可以枚举其约数,即枚举与另一个数 \(a_j\)\(\gcd\)。这样的话,\(cnt(a_i)\)\(cnt(\gcd(a_i,a_j))\) 就都确定下来了。要使 \(d(a_i,a_j)\) 最小,只需 \(cnt(a_j)\) 最小,于是可以处理出每个数的所有倍数中 \(cnt\) 最小的数,且该数在序列 \(\{ a_i \}\) 中。因为要求 \(i \not = j\),因此还需处理出次小值。

然后就可以统计每个约数的最优贡献了,这样枚举到的约数不一定恰好为 \(\gcd(a_i,a_j)\),但当枚举到 \(\gcd(a_i,a_j)\) 一定会更优,所以最终的答案一定为 \(\gcd(a_i,a_j)\)

#include<bits/stdc++.h>
#define maxn 1000010
#define all 1000000
#define inf 1000000000
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,tot,ans,val;
int a[maxn],p[maxn],cnt[maxn],mn1[maxn],mn2[maxn];
bool tag[maxn];
void init()
{
    for(int i=2;i<=all;++i)
    {
        if(!tag[i]) p[++tot]=i,cnt[i]=1;
        for(int j=1;j<=tot;++j)
        {
            int k=i*p[j];
            if(k>all) break;
            tag[k]=true,cnt[k]=cnt[i]+1;
            if(i%p[j]==0) break;
        }
    }
}
void update(int d,int x)
{
    if(cnt[a[x]]<cnt[a[mn1[d]]]) mn2[d]=mn1[d],mn1[d]=x;
    else if(cnt[a[x]]<cnt[a[mn2[d]]]&&x!=mn1[d]) mn2[d]=x;
}
void work(int d,int x)
{
    int y=mn1[d]==x?mn2[d]:mn1[d],v=cnt[a[x]]+cnt[a[y]]-2*cnt[d];
    if(v<val||(v==val&&y<ans)) val=v,ans=y;
}
int main()
{
    init(),read(n),cnt[0]=inf;
    for(int i=1;i<=n;++i) read(a[i]);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j*j<=a[i];++j)
        {
            if(a[i]%j) continue;
            update(j,i),update(a[i]/j,i);
        }
    }
    for(int i=1;i<=n;++i)
    {
        val=inf;
        for(int j=1;j*j<=a[i];++j)
        {
            if(a[i]%j) continue;
            work(j,i),work(a[i]/j,i);
        }
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2020-09-07 16:32  lhm_liu  阅读(359)  评论(0编辑  收藏  举报