【XJOI】数字转换

题目链接

数字转换

题目描述:

如果一个数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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值