題解 洛谷 P3532 【[POI2012]ODL-Distance】
阿新 • • 發佈:2020-09-07
設 \(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\)
然後就可以統計每個約數的最優貢獻了,這樣列舉到的約數不一定恰好為 \(\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; }