卧薪尝胆,厚积薄发。
POI2012 ODL-Distance
Date: Tue Oct 30 17:26:21 CST 2018
In Category:
NoCategory
Description:
给一个序列
$a[]$
,定义
$d(i,j)$
为每次对
$a[i],a[j]$
之一乘一个质数
$p$
或除以一个质数
$p$
,让
$a[i]=a[j]$
的最少操作步数。
对于每个
$i$
,求
$d(i,j)$
最小的
$j$
,若有多个解,输出最小的j
$1\leqslant n\leqslant 10^5,1\leqslant a[i]\leqslant 10^6$
Solution:
首先发现
$d(i,j)=\frac{a[i]}{\gcd(a[i],a[j])}\times \frac{a[j]}{\gcd(a[i],a[j])}$
,求出来
$div[i]$
表示
$i$
分解成的质数个数,那么有
$d(i,j)=div[a[i]]+div[a[j]]-2\times div[\gcd(a[i],a[j])]$
,那么我们可以枚举
$k$
作为
$\gcd(a[i],a[j])$
,再枚举一个
$k$
的倍数作为
$a[i]$
,这样我们只要最小化
$div[a[j]]$
即可,那么我们可以再预一个
$div[a[j]]$
的最小值和次小值来更新,因为不能选重复的,但是这样可能
$k\ne \gcd(a[i],a[j])$
,但是没关系,因为这样的话一定不如
$k'=\gcd(a[i],a[j])$
时的值优,所以必然不会成为最后的答案,注意在预处理最大值和次大值的时候细节很多。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<vector>
#include<cctype>
#include<cstring>
using namespace std;
int n;
#define MAXN 100010
int a[MAXN];
#define MAX 1000001
bool isprime[MAX];
int prime[MAX],tot = 0;
int mindiv[MAX];
int d[MAX];
void sieve()
{
for(int i = 2;i < MAX;++i)isprime[i] = true;
for(int i = 2;i < MAX;++i)
{
if(isprime[i])
{
mindiv[i] = i;
prime[++tot] = i;
}
for(int j = 1;j <= tot && i * prime[j] < MAX;++j)
{
isprime[i * prime[j]] = false;
mindiv[i * prime[j]] = prime[j];
if(i % prime[j] == 0)break;
}
}
for(int i = 2;i < MAX;++i)
{
d[i] = d[i / mindiv[i]] + 1;
}
return;
}
int gcd(int a,int b){return (b == 0 ? a : gcd(b,a % b));}
vector<int> v[MAX];
int ans[MAX],res[MAX];
bool smaller(int x,int y)
{
if(d[a[x]] < d[a[y]])return true;
if(d[a[x]] > d[a[y]])return false;
if(x < y)return true;
else return false;
}
int main()
{
sieve();
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
for(int i = 1;i <= n;++i)
{
v[a[i]].push_back(i);
}
memset(ans,0x3f,sizeof(ans));
for(int i = 1;i < MAX;++i)
{
int fi = 0,se = 0;
for(int j = i;j < MAX;j += i)
{
for(vector<int>::iterator it = v[j].begin();it != v[j].end();++it)
{
if(fi == 0)fi = *it;
else
{
if(smaller(*it,fi))se = fi,fi = *it;
else if(se == 0 || smaller(*it,se))se = *it;
}
}
}
if(se == 0)continue;
for(int j = i;j < MAX;j += i)
{
for(vector<int>::iterator it = v[j].begin();it != v[j].end();++it)
{
if(*it == fi)
{
if(d[a[*it]] + d[a[se]] - 2 * d[i] < ans[*it])
{
ans[*it] = d[a[*it]] + d[a[se]] - 2 * d[i];
res[*it] = 0x3f3f3f3f;
}
if(d[a[*it]] + d[a[se]] - 2 * d[i] == ans[*it])
res[*it] = min(res[*it],se);
}
else
{
if(d[a[*it]] + d[a[fi]] - 2 * d[i] < ans[*it])
{
ans[*it] = d[a[*it]] + d[a[fi]] - 2 * d[i];
res[*it] = 0x3f3f3f3f;
}
if(d[a[*it]] + d[a[fi]] - 2 * d[i] == ans[*it])
res[*it] = min(res[*it],fi);
}
}
}
}
for(int i = 1;i <= n;++i)printf("%d\n",res[i]);
return 0;
}
In tag:
数学-线性筛
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡