BZOj2790/POI2012 Distance

Task
对于两个正整数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)最小。
2<=n<=100,000, Ai<=1,000,000.

Solution
设cnt[i]为i的质因子个数,d(a,b)=cnt[a]-cnt[b]-2*cnt[gcd(a,b)],对于每个i,考虑枚举A[i]与A[j]的gcd,假设为k.剩下不确定的答案就是A[j]了,为了让cnt[A[j]]尽可能小,可以预处理出dp[k],表示k的倍数中cnt最小的下标t.为了防止t=i,对于每个k,需要求出前两小的下标.
Ai≤10^6,因子个数最多为240个,可以直接暴力枚举每个值的所有因子进行求解.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
#include<queue>
#include<set>
using namespace std;
inline void rd(int &res){
    res=0;char c;
    while(c=getchar(),c<48);
    do res=(res<<1)+(res<<3)+(c^48);
    while(c=getchar(),c>=48);
}
inline void print(ll x){
    if(!x)return ;
    print(x/10);
    putchar((x%10)^48);
}
inline void sc(ll x){
    if(x<0){x=-x;putchar('-');}
    print(x);
    if(!x)putchar('0');
    putchar('\n');
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
const int M=1e6+5;
const int N=1e5+5;
int mark[M],son[M][10],cnt[M][10],A[N],mx=0;
vector<int>rec[M];
struct node{
    int v,id;
    node(){id=-1,v=-1;}
    bool operator<(const node &tmp)const{
        if(tmp.v!=v)return v<tmp.v;
        return id<tmp.id;
    }
}h[M][2],ans[N];
void Init(){
    for(int i=2;i*i<=mx;i++){
        if(!mark[i]){
            for(int j=i;j<=mx;j+=i){
                son[j][++son[j][0]]=i;
                mark[j]=1;
            }
        }
    }
}
void Up(int x,node y){//y是x的倍数    
//  if(h[x][0].id==-1)h[x][0]=y;
    if(h[x][0].id==-1||y<h[x][0]){
        h[x][1]=h[x][0];
        h[x][0]=y;
    }
    else if(h[x][1].id==-1||y<h[x][1]){
        h[x][1]=y;
    }
}
void dfs(int id,int x,int p,int num){
    if(p==son[x][0]+1){
        node y;
        y=h[num][0];
        if(h[num][0].id==id)y=h[num][1];
        if(~y.v){
            y.v+=cnt[x][0]-2*cnt[num][0];
            if(ans[id].id==-1||y<ans[id])ans[id]=y;
        }
        return;
    }
    for(int i=0;i<=cnt[x][p];i++){
        dfs(id,x,p+1,num);
        num*=son[x][p];
    }
}
int main(){
    int n,i,j,cas,a,b,k;
    rd(n);
    for(i=1;i<=n;i++){
        rd(A[i]);Max(mx,A[i]);
        rec[A[i]].push_back(i);
    }
    Init();
    for(i=2;i<=mx;i++){
        int x=i;
        for(j=1;j<=son[i][0];j++){
            a=0;
            while(x%son[i][j]==0){a++;x/=son[i][j];}
            cnt[i][j]=a;
            cnt[i][0]+=a;
        }
        if(x!=1){
            son[i][++son[i][0]]=x;
            cnt[i][son[i][0]]=1;
            cnt[i][0]++;
        }
    }
    for(i=1;i<=mx;i++){
        for(j=i;j<=mx;j+=i){
            if(!rec[j].size())continue;
            node y;
            y.v=cnt[j][0];
            for(k=0;k<rec[j].size();k++){
                y.id=rec[j][k];
                Up(i,y);
            }
        }   
    }
    for(i=1;i<=n;i++)dfs(i,A[i],1,1);
    for(i=1;i<=n;i++)sc(ans[i].id);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值