辽宁OI2016夏令营模拟T1-dis

数值距离(dis.pas/c/cpp)
题目大意
我们可以对一个数 x 进行两种操作:
1、 选择一个质数 y,将 x 变为 x*y
2、 选择一个 x 的质因数 y,将 x 变为 x/y
定义两个数 a,b 之间的距离为把 a 变成 b 所需要执行的最少操作次数。例如数 69 与 42
之间的距离为 3,因为 42=69/23*2*7
现在有一个长度为 n 的序列 a1,a2,…,an。对于每一个 i,我们需要找到一个 j,使得 ai 到
aj 的距离最小,若有多个 j 满足条件,输出最小的 j。
输入文件
输入文件为 dis.in。
输入共有 n+1 行,第一行有一个数 n,接下来 n 行每行一个数 ai。
输出文件
输出文件为 dis.out。
输出一共 n 行,每行一个整数,第 i 行的整数表示对于 i 所求得的 j 是多少。
样例输入
6
1
2
3
4
5
6
样例输出
2
1
1
2
1
2
数据规模与约定
对于 30%的数据,n≤1000;
另有 20%的数据,ai≤1000;
对于 100%的数据,2≤n≤100000,1≤ai≤1000000。

——————————————————题解

这道题的暴力思路就是枚举每两个数,然后算这两个数的gcd,然后两个数的分解质因数个数的和减去2*gcd分解质因数的个数就是距离,找最小,这是(n^2logn)的

大概12,16这两个数,gcd是4,4=2*2,12=2*2*3,16=2*2*2*2,12和16的距离就是(4+3)-2*2

这样就可以轻松愉快的拿到30分了!【然而我很弱,爆了0……orz】

然后我们发现每两个数的距离(不一定是最小距离)可以写作这两个数分解质因数个数和减去他们俩共同的约数的分解质因数个数

我们可选择不枚举数,而是枚举他们的约数,显然约数为gcd的时候距离这小,但这会在不断更新约数的时候被更新到。

设约数为k,然后我们在a1,a2...an的序列里找到k的倍数并向k连一条边,由于a约数的对数不会超过a^0.5,所以我们枚举的次数不会超过10^3n (10^6^0.5)

然后我们暴力的枚举到一个约数离它最近的最小数和次小数,次小数更新最小数(因为最小数不能自我更新),最小数更新其他数。也是10^3n的。

这道题输出的是下标!!!这道题输出的是下标!!!这道题输出的是下标!!!这道题输出的是下标!!!orz自己真是越来越瞎了

还有要学会用pair……

【注意:这里的ans并非输出的答案,而是最小的距离】

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <vector>
 5 #include <queue>
 6 #include <cmath>
 7 #define siji(i,x,y) for(int i=(x);i<=(y);i++)
 8 #define gongzi(j,x,y) for(int j=(x);j>=(y);j--)
 9 #define xiaosiji(i,x,y) for(int i=(x);i<(y);i++)
10 #define sigongzi(j,x,y) for(int j=(x);j>(y);j--)
11 #define pii pair<int,int>
12 #define fi first
13 #define se second
14 using namespace std;
15 vector<pii> list[1000005];
16 int n,num[100005];
17 pii ans[100005];
18 int factor[1000005],prime[100005],cnt;
19 bool isprime[1000005];
20 void init() {
21     scanf("%d",&n);
22     siji(i,2,1000000) {
23         if(!isprime[i]) {factor[i]=1;prime[++cnt]=i;}
24         for(int j=1;prime[j]*i<=1000000;j++) {
25             isprime[prime[j]*i]=1;
26             factor[prime[j]*i]=factor[prime[j]]+factor[i];
27             //质因子是什么不重要,我们只要知道个数就可以了
28             if(i%prime[j]==0) break;
29         }
30     }
31     siji(i,1,n) {
32         scanf("%d",&num[i]);
33         ans[i].fi=0x1f1f1f1f;
34         int t=(int)sqrt(num[i]);//从1-a[i]^0.5枚举看看那些是它的约数,是它的约数就有可能是它和别的数的gcd
35         siji(j,1,t) {
36             if(num[i]%j==0 && j*j!=num[i]) {
37                 list[j].push_back(pii(factor[num[i]/j],i));
38                 list[num[i]/j].push_back(pii(factor[j],i));
39             }
40             else if(num[i]%j==0 && j*j==num[i]) {
41                 list[j].push_back(pii(factor[j],i));
42             }
43         }
44     }
45     
46 }
47 
48 void solve() {
49     siji(i,1,1000000) {
50         if(list[i].size()>1) {
51             pii t1=list[i][0],t2=list[i][1];//讲真pair会省掉很多比较的麻烦,orz蒟蒻的自言自语罢了
52             //在比较时first的权重较大,所以first小的在前面,first相同再是second,second小的在前面,perfect
53             if(t2<t1) swap(t1,t2);
54             int s=list[i].size();
55             xiaosiji(j,2,s) {
56                 if(t2>list[i][j]) t2=list[i][j];
57                 if(t2<t1) swap(t1,t2);
58             }
59             xiaosiji(j,0,s) {
60                 if(list[i][j]!=t1) {
61                     ans[list[i][j].se]
62                     =min(ans[list[i][j].se],pii(t1.fi+list[i][j].fi,t1.se));
63                 }
64             }
65             ans[t1.se]=min(ans[t1.se],pii(t1.fi+t2.fi,t2.se));
66 
67         }
68     }
69     siji(i,1,n) {
70         printf("%d
",ans[i].se);
71     }
72 }
73 int main(int argc, char const *argv[])
74 {
75     freopen("dis.in","r",stdin);
76     freopen("dis.out","w",stdout);
77     init();
78     solve();
79     return 0;
80 }
原文地址:https://www.cnblogs.com/ivorysi/p/5795223.html