题目链接
http://main.edu.pl/en/archive/oi/19/odl
题目大意
给你一个序列
a[]
,定义
d(i,j)=
a[i],a[j]
每次操作可以对其中之一乘一个质数
p
或除以一个数
对于每个
i
,求
思路
显然, d(i,j)=cnt[a[i]]+cnt[a[j]]−2cnt[gcd(a[i],a[j])](cnt[i]=数字i的质因数个数) ,即最少操作步数的方案是,先让 a[i] 变成 gcd(a[i],a[j]) ,然后再乘上若干次质数,变成 a[j] 。
我们可以预处理求出
f[i]=
是
i
的倍数,且
对于每次询问
i
,我们可以枚举
由于这样做,可能出现
i=j
的情况,因此我们要判断
f[gcd(i,?)]
是否为
i
,并且要同时记录维护
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 110000
using namespace std;
int n,m;
struct edge
{
int u,v,next;
}edges[MAXN*2];
int head[MAXN],nCount=0;
void AddEdge(int U,int V)
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].next=head[U];
head[U]=nCount;
}
int fa[MAXN][22],depth[MAXN];
void LCA_prework()
{
for(int i=1;i<=n;i++)
for(int j=1;j<20;j++)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
void LCA(int a,int b)
{
if(depth[a]<depth[b]) swap(a,b);
for(int i=19;i>=0;i--)
{
if(depth[fa[a][i]]>=depth[b]) continue;
a=fa[a][i];
}
if(depth[a]!=depth[b]) a=fa[a][0];
if(a==b) return a;
for(int i=19;i>=0;i--)
{
if(fa[a][i]==fa[b][i]) continue;
a=fa[a][i],b=fa[b][i];
}
a=fa[a][0],b=fa[b][0];
return a;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&fa[i][0]);
AddEdge(fa[i][0],i);
}
return 0;
}