数字转换
题目描述:
如果一个数x的约数和(不包括它本身,下同)比它本身小,那么x可以变成它的约数和;如果对于某个y>x且y的约数和为x,那么x也可以变成y。例如,4可以变为3,1可以变为7。限定所有的数字变换在不超过n的正整数范围内进行,求不断进行数字变换且没有重复数字出现的最多变换步数。
输入格式:
输入一个正整数n。
输出格式:
输出最少需要花费的时间。
样例输入:
7
样例输出:
3
数据范围:
\(n<=50,000\)。
时间限制:
1000
空间限制:
32768
提示:
remove!!!
题解
博主刚开始打时被卡题意了o(╥﹏╥)o,看成了要从\(n\)开始转换。
假设\(i\)可以变成\(j\)(\(j<i\)),那么\(j\)也可以变成\(i\)。
那么\(i\)和\(j\)之间可以连一条双向边。
因为每个数的约数和是惟一的,而且每个数能变成他的约数和的条件是它的约数和小于它本身,所以把这些可以转移的约数和排列起来,有没有像输入一棵树时给你每个节点的父亲节点?
那么我们就可以把这些点转换为一棵树,问题就变成了求树的直径。
树的直径怎么求?两遍dfs就好了,萌新请自行学习,博主就不在这里赘述了。
上代码:
#include<bits/stdc++.h>
#include<vector>
using namespace std;
int n;
int to[300009];
int ans;
struct aa{
int nxt,to;
}p[50009];
int l,h[50009];
bool k[500009];
int uu,ux;
void add(int x,int y){
l++;
p[l].nxt=h[x];
p[l].to=y;
h[x]=l;
}
void dfs1(int x,int u){
if(u>uu){
uu=u;
ux=x;
}
k[x]=1;
for(int j=h[x];j;j=p[j].nxt){
if(k[p[j].to]==0) dfs1(p[j].to,u+1);
}
k[x]=0;
}
void dfs2(int x,int u){
ans=max(ans,u);
k[x]=1;
for(int j=h[x];j;j=p[j].nxt)
if(k[p[j].to]==0) dfs2(p[j].to,u+1);
k[x]=0;
}
int main(){
scanf("%d",&n);
for(int j=1;j<=n;j++){
int i=2;
while(j*i<=n){
to[j*i]+=j;
i++;
}
if(j!=1 && to[j]<j){
add(j,to[j]);
add(to[j],j);
}
}
dfs1(1,0);
dfs2(ux,0);
printf("%d",ans);
return 0;
}