数字转换 LibreOJ - 10155树的直径 数论

如果一个数 xx 的约数(不包括他本身)的和 yy 比他本身小,那么 xx 可以变成 yy,yy 也可以变成 xx。例如 44 可以变为 33,11 可以变为 77。限定所有数字变换在不超过 nn 的正整数范围内进行,求不断进行数字变换且不出现重复数字的最多变换步数。

Input

输入一个正整数 nn。

Output

输出不断进行数字变换且不出现重复数字的最多变换步数。

Example

样例输入

7

样例输出

3

样例说明

一种方案为 43174→3→1→7。

Hint

对于 100%100% 的数据,1n500001≤n≤50000。

思路

求树的最长链

d1[i]d1[i]为以ii为根的子树中,i到叶子节点距离最大值

d2[i]d2[i]为以ii为根的子树中,i的叶子节点距离次大值(除了最大值所在的子树)

若j为i的儿子,那么:

  • d1[j]+dis[i][j]>d1[i]d1[j]+dis[i][j]>d1[i],则d2[i]=d1[i];d1[i]=d2[j]=dis[i][j];d2[i]=d1[i];d1[i]=d2[j]=dis[i][j];
  • 否则,若d1[j]+dis[i][j]>d2[i]d1[j]+dis[i][j]>d2[i],则d2[i]=d1[j]+dis[i][j];d2[i]=d1[j]+dis[i][j];

最后扫描所有节点,最长链=max{d1[i]+d2[i]}

#include <bits/stdc++.h>
using namespace std;
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
inline void write(int zx){
    if(zx<0){zx=-zx;putchar('-');}
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
int sum[500001],n,d1[500001],d2[500001],ans;
void Pri(){
    for(int i=1;i<=n;i++){
        for(int j=2;j<=n/i;j++){
            if(i*j>n) break;
            sum[i*j]+=i;
        }
    }
}
void dp(){
    for(int i=n;i>=1;i--){
        if(sum[i]<i){
            if(d1[i]+1>d1[sum[i]]){
                d2[sum[i]]=d1[sum[i]];
                d1[sum[i]]=d1[i]+1;
            }else if(d1[i]+1>d2[sum[i]]) d2[sum[i]]=d1[i]+1;
        }
    }
}
int main(){
    n=read();
    Pri();
    dp();
    for(int i=1;i<=n;i++) ans=max(ans,d1[i]+d2[i]);
    write(ans);putchar('\n');
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xxxsans/p/12747877.html