P3805距离 | ||
|
问题描述
对于两个正整数a、b,这样定义函数d(a,b):每次操作可以选择一个质数p,将a变成a*p或a/p,
如果选择变成a/p就要保证p是a的约数,d(a,b)表示将a变成b所需的最少操作次数。
例如d(69,42)=3。
现在给出n个正整数A1,A2,...,An,对于每个i (1<=i<=n),求最小的j(1<=j<=n)使得i≠j且d(Ai,Aj)最小。
输入格式
第一行一个正整数n (2<=n<=100,000)。
第二行n个正整数A1,A2,...,An (Ai<=1,000,000)。
输出格式
输出n行,依次表示答案。
样例输入
6
1
2
3
4
5
6
样例输出
2
1
1
2
1
2
来源
poi 2012 distance
这道题我们先想一下怎么暴力做
如果要暴力,设g[i]为i的所有质因数的数量,我们首先推出d(a,b)=g[a]+g[b]-2*g[gcd(a,b)],然后枚举每一个a,b然后更新答案就可以了
这里的g[i]是很好求出的
但是考虑到n的数据,我们肯定不能直接暴力
枚举a[i],由于g[a[i]]相对不变,我们需要找到g[a[j]]-2*g[gcd(a[i],a[j])]的最小值
因此我们用f[x]表示因数为x的倍数a[j]使得g[a[j]]最小
由于要求i!=j因此我们维护一个最小值和次小值,所以f数组要开两维
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=1e5+5,maxm=1e6+5,inf=1e9;
int n,a[maxn],f[maxm][2],g[maxm],maxx=0,prime[maxn],tot;
int ans,curid;
inline void _read(int &x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9')
{if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
void g_table(){//打印g数组
int i,j;
g[0]=inf;
for(i=2;i<=maxx;i++){
if(!g[i])prime[++tot]=i,g[i]=1;
for(j=1;j<=tot&&i*prime[j]<=maxx;j++){
g[i*prime[j]]=g[i]+1;
if(!(i%prime[j]))break;
}
}
}
void update(int x,int k){更新f[][]数组
if(g[a[x]]<=g[a[f[k][0]]])f[k][1]=f[k][0],f[k][0]=x;
else if(g[a[x]]<=g[a[f[k][1]]])f[k][1]=x;
}
void ok(int i,int j){
int k=j,x;
if(f[k][0]!=i)x=f[k][0];
else x=f[k][1];
int temp=g[a[x]]-2*g[k];
if(temp<ans||(temp==ans&&x<curid))ans=temp,curid=x;
k=a[i]/j;
if(f[k][0]!=i)x=f[k][0];
else x=f[k][1];
temp=g[a[x]]-2*g[k];
if(temp<ans||(temp==ans&&x<curid))ans=temp,curid=x;
}
int main(){
_read(n);
int i,j;
for(i=1;i<=n;i++){
_read(a[i]);
maxx=max(a[i],maxx);
}
g_table();
for(i=n;i;i--)
for(j=1;j*j<=a[i];j++)
if(a[i]%j==0){
update(i,j);
if(j*j!=a[i])
update(i,a[i]/j);
}
for(i=1;i<=n;i++){
ans=inf,curid=inf;
for(j=1;j*j<=a[i];j++)
if(!(a[i]%j))ok(i,j);
printf("%d\n",curid);
}
}