数值距离

题目描述
我们可以对一个数进行两种操作,即把一个数乘上一个质数或者除以一个质
数。我们定义两个数 a,b 之间的距离为把 a 变成 b 需要执行的最少操作次数,例
如 d(69,42)=3,因为 42=69/23*2*7。
我们给出一个长度为 n 的序列 a 1 ,a 2 ,...,a n ,对于每一个 a i 我们需要找到一个
不等于 i 的最小的 j 使得 a i 和 a j 的距离是最小的。
输入格式
第一行一个整数 n。
接下来 n 行,第 i 行一个整数 a i 。
输出格式
n 行,第 i 行一个整数,表示 d(ai,aj)最小时最小的 j。
输入样例
6
1
2
3
4
5
6
输出样例
2
1
1
2
1
2

数据规模
30%的数据,n<=1000。
另外 20%的数据,a i <=1000。
100%的数据,2<=n<=100000,1<=a i <=1000000。

我们看到ai<=1000000,正常情况下,如果复杂度与a没关系应该是10^9

考虑以ai为点建图,如果两个数可以通过一次变换就建边

用ai做起点,如果ai为起点的路径碰上了以aj为起点的路径,那么就要更新ai和aj的答案

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 using namespace std;
 8 typedef long long lol;
 9 lol n;
10 lol vis[1000001],v[1000001],dist[1000001],tot,Min[1000001],pos[1000001],a[1000001],pre[1000001],prime[1000001];
11 void zyys(int x,int y)
12 {
13     if (v[x]==v[y]) return;
14     int now=dist[x]+dist[y]+1;
15     if (Min[v[x]]==now) pos[v[x]]=min(pos[v[x]],v[y]);
16     if (Min[v[y]]==now) pos[v[y]]=min(pos[v[y]],v[x]);
17     if (Min[v[x]]>now) Min[v[x]]=now,pos[v[x]]=v[y];
18     if (Min[v[y]]>now) Min[v[y]]=now,pos[v[y]]=v[x];
19     if (dist[x]==dist[y]+1)
20         v[x]=min(v[x],v[y]);
21 }
22 void SPFA()
23 {
24     int i;
25     queue<int>Q;
26     memset(dist,127/2,sizeof(dist));
27     for (i=1; i<=n; i++)
28     {
29         if (v[a[i]]==i) Q.push(a[i]);
30     }
31     while (Q.empty()==0)
32     {
33         int u=Q.front();
34         Q.pop();
35         int b=u;
36         while (b!=1)
37         {
38             int now=u/pre[b];
39             if (v[now]==0) v[now]=v[u],dist[now]=dist[u]+1,Q.push(now);
40             else zyys(now,u);
41             b/=pre[b];
42         }
43         for (i=1; i<=tot&&1ll*u*prime[i]<=1000000; i++)
44         {
45             int now=u*prime[i];
46             if (v[now]==0) v[now]=v[u],dist[now]=dist[u]+1,Q.push(now);
47             else zyys(now,u);
48         }
49     }
50 }
51 int main()
52 {
53     lol i,j,d,ans;   56     cin>>n;
57     vis[1]=1;
58     for (i=2; i<=1000000; i++)
59     {
60         if (vis[i]==0)
61         {
62             ++tot;
63             prime[tot]=i;
64             pre[i]=i;
65         }
66         for (j=1; j<=tot; j++)
67         {
68             if (1ll*i*prime[j]>1000000) break;
69             vis[i*prime[j]]=1;
70             pre[i*prime[j]]=prime[j];
71             if (i%prime[j]==0) break;
72         }
73     }
74     memset(Min,127/2,sizeof(Min));
75     memset(pos,127/2,sizeof(pos));
76     for (i=1; i<=n; i++)
77     {
78         scanf("%lld",&a[i]);
79         if (v[a[i]]==0) v[a[i]]=i;
80         else
81         {
82             Min[i]=Min[v[a[i]]]=0;
83             pos[i]=min(pos[i],v[a[i]]);
84             pos[v[a[i]]]=min(pos[v[a[i]]],i);
85         }
86     }
87     SPFA();
88     for (i=1; i<=n; i++)
89         printf("%lld\n",pos[i]);
90 }

 

转载于:https://www.cnblogs.com/Y-E-T-I/p/8602374.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值