【题】【数学】NKOJ3805 距离

17 篇文章 0 订阅

NKOJ3805 距离
时间限制 : - MS 空间限制 : 165536 KB
评测说明 : 3000ms

问题描述
对于两个正整数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

设cnt[x]表示x包含的质因数的个数。 则,d(a,b)=cnt[a]+cnt[b]-2*cnt[gcd(a,b)]

gcd(a,b)可能不在给出数中,所以要先处理1~max(a[i])的所有cnt。->线性筛

对于每一个固定的a,要求cnt[b]-2*cnt[gcd(a,b)]最小。-> 对于ai的每个因数x,预处理出它的倍数中,cnt[]值最小的j。
最小的j可能等于i,解决方法->在记一次次小值

#include<cstdio>
#include<iostream>
using namespace std;
const int needn=100003;
const int needm=1000003;

int a[needn],n;
int pr[needm],tot;
int cnt[needm]={1e9,},nnn;

inline void in_(int &d)
{
    char t=getchar();
    while(t<'0'||t>'9') t=getchar();
    for(d=0;t<='9'&&t>='0';t=getchar()) d=(d<<1)+(d<<3)+t-'0';
}

void get_cnt()
{
    for(int i=2,j,temp;i<=nnn;i++)
    {
        if(cnt[i]==0)
        {
            pr[++tot]=i;
            cnt[i]=1;
        }
        for(j=1;j<=tot&&(temp=pr[j]*i)<=nnn;j++)
        {
            cnt[temp]=cnt[i]+1;
            if(i%pr[j]==0) break;
        }
    }
}

int f1[needm],f2[needm];//f1,最小的,f2次小

int change(int kk,int k) //处理第kk号数字,因数为k的情况
{
    if(cnt[a[kk]]<cnt[a[f1[k]]]||(cnt[a[kk]]==cnt[a[f1[k]]]&&(kk<f1[k]||f1[k]==0)))
    {
        f2[k]=f1[k];
        f1[k]=kk;
    }
    else if(cnt[a[kk]]<cnt[a[f2[k]]]||(cnt[a[kk]]==cnt[a[f2[k]]]&&(kk<f2[k]||f2[k]==0))) f2[k]=kk;

}

void solve_f()//预处理f1、f2
{
    for(int i=1,j,kk;i<=n;i++)
    {
        for(j=1;j*j<=a[i];j++)
        if(a[i]%j==0)
        {
            change(i,j);
            if(j*j!=a[i]) change(i,a[i]/j);
        } 
    }
}

int get_f(int i,int k)
{
    return f1[k]!=i ? f1[k] : f2[k];
}


int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) in_(a[i]),nnn=max(nnn,a[i]);
    get_cnt();
    solve_f();
    for(int i=1,j,k,ans,tk,tans,x;i<=n;i++)
    {
        ans=1e9,k=0;
        for(int j=1;j*j<=a[i];j++)
        {
            if(a[i]%j==0)
            {
                tk=get_f(i,j);
                tans=cnt[a[tk]]-2*cnt[j];
                if(tans<ans||(tans==ans&&tk<k)) ans=tans,k=tk;
             if(j*j!=a[i])
             {
                x=a[i]/j;
                tk=get_f(i,x);
                tans=cnt[a[tk]]-2*cnt[x];
                if(tans<ans||(tans==ans&&tk<k)) ans=tans,k=tk;
             }
            }
        }
        printf("%d\n",k);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值