对于一个数 x 而言,其约数和 y 是固定的,但反过来一个 y 却可以有多个 x 与其对应
因此如果在考虑建图的时候,我们应当是建立从 y 指向 x 的有向边
本题要求最长的转换步数,因此相当于是求一个有向图中的最长路径,直接用 AcWing 1072. 树的最长路径 来做就行,有一点区别的是,树的最长路径是无向图,本题是有相图,也就是不需要 father
这个变量(这个的存在就是为了防止往回走导致死循环)
剩下的问题就是关于如何建图了
在这里,我们需要求出 2∼n 中每个数的约数和(不能包括 1 ),直接用试除法来做的话复杂度为 O(n√n),这里我们采用筛法来做
我们遍历 1∼n 中的每个数 i ,考虑它们是哪些数的约数(设为 x),这样便得到了 x 的约数,再将所有的 x 的约数加起来即可
完整代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 5e4 + 10;
int h[N], e[N], ne[N], idx;
int sum[N];
bool st[N];
int n, ans;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u)//有向边,不需要father
{
int d1 = 0, d2 = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
int d = dfs(j) + 1;
if(d >= d1) d2 = d1, d1 = d;
else if(d > d2) d2 = d;
}
ans = max(ans, d1 + d2);//用经过这个点的最长路径来更新ans
return d1;
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);
for(int i = 1; i <= n; i++)
for(int j = 2; j <= n / i; j++)
sum[j * i] += i;
for(int i = 2; i <= n; i++)//这里不能从1开始,因为1的sum[i]为0,而0不能作为约数存在
if(sum[i] < i)//这个判断一定要有
{
add(sum[i], i);//从约数和指向原数的一条有向边,因为一个数的约数和只有一个,反过来却可以有很多个
st[i] = true;
}
for(int i = 1; i <= n; i++)//这里必须从1开始枚举,因为1可以作为任意数的约数
if(!st[i])
dfs(i);
cout << ans << endl;
return 0;
}