【LOJ10155】数字转换

题目链接

数字转换

题目描述:

如果一个数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;
}
原文地址:https://www.cnblogs.com/linjiale/p/11277278.html