题解
- 首先,对于小于n的每个数,我们可以确定它的约数之和(不包括自己)是固定的,就像4的约数之和一定是3,不可能是其他的,那么我们就可以将2-n的每个数的约数之和求出sum[i],对于sum[i]<i 的我们连一条sum[i]---->i 的边(因为对于每个i,sum[i]是唯一确定的),也就是说每个儿子都有唯一一个父节点,那么我们最终就会构成森林(多课树),题中找最长的变换步骤就转换成了求树的最长直径
- 为什么不从1开始 ,因为1除本身外约数之和就是0,题中要求最小为1,所以1是不符合要求的
- 如何找一树的最长直径,我们可以依次枚举每个点,找这个点最长和次长距离,然后相加即可 ,具体可看树的最长路径,这里就不详细讲解了
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 5e4 + 10;
int n;
int h[N], e[N], ne[N], idx;
int sum[N];
bool is[N];
int ans;
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int dfs(int u) {
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);
return d1;
}
int main() {
memset(h, -1, sizeof h);
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 2; j <= n / i; j++) {
sum[i * j] += i;
}
}
for (int i = 2; i <= n; i++) {
if (sum[i] < i) {
add(sum[i], i);
}
is[i] = true;
}
for (int i = 1; i <= n; i++) {
if (!is[i]) {
dfs(i);
}
}
cout << ans << endl;
return 0;
}