题目描述
如果一个数 x的约数和 y(不包括他本身)比他本身小,那么 x 可以变成 y,y 也可以变成 x。例如 4 可以变为 3,1 可以变为 7。限定所有数字变换在不超过 n 的正整数范围内进行,求不断进行数字变换且不出现重复数字的最多变换步数。
输入格式
输入一个正整数 n。
输出格式
输出不断进行数字变换且不出现重复数字的最多变换步数。
样例
样例输入
7
样例输出
3
样例说明
一种方案为 4→3→1→7。
数据范围与提示
对于 100%的数据,1≤n≤50000。
首先预处理出小于N的每个数的约数和sum[i]。
对于每个数i,如果sum[i]<i,那么i和sum[i]可以互相转化,即两点间连一条边,i为sum[i]的一个儿子。
显然最后的结果是一棵树,问题就转化为了求这棵树的直径。
一棵树的直径被根节点分为两段,分别是根结点到距离根最远的两个叶子。
用dis[i]表示以i为根的子树到叶子结点的最长路径,dis2表示次长路径。
对于每个点i,更新其子结点的dis和dis2即可。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 100010
using namespace std;
int sum[maxn],dis[maxn],dis2[maxn],n,cnt,ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
for(int j=2;j<=n/i;j++){
if(i*j>n) break;
sum[i*j]+=i;
}
}
for(int i=n;i>=1;i--){
if(sum[i]<i){
int j=sum[i];
if(dis[i]+1>dis[j]){
dis2[j]=dis[j];
dis[j]=dis[i]+1;
}
else if(dis[i]+1>dis2[j]) dis2[j]=dis[i]+1;
}
}
for(int i=1;i<=n;i++) if(dis[i]+dis2[i]>ans) ans=dis[i]+dis2[i];
cout<<ans;
return 0;
}