数字转换(求树的直径)

传送门

思路:


 

前置知识——树的直径:

▲定义:找到两个点,使得它们的距离最远,它们的路径就是树的直径。

▲求解:

  此类问题,需要求出以每个节点为根的子树中的最长链,取其中的最大值即为该树的最长链。

  对于每个节点,都要记录两个值: d1 [ i ] 表示以 i 为根的子树中,i 到叶子节点的距离最大值;d2 [ i ] 表示以 i 为根的子树中,除距离最大值所在子树,i 到叶子节点的最大值。(也就是次大值)。则:

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

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

  最后扫描所有的节点,找最大的 d1 [ i ] + d2 [ i ] 的值,就是树的直径(最长链)。


本题思路:

  预处理出每个数的约数,将可以转化的数之间连边。就可以建出一棵树,而所求的最多变换步数即为树的直径。

标程:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<stack>
#include<vector>
#include<queue>
#include<deque>
#include<map>
#include<set>
using namespace std;
#define maxn 50001
#define max(a, b) ((a)>(b)?(a):(b))
typedef long long LL;
LL sum[maxn],n,d1[maxn],d2[maxn];//sum预处理每个数的约数 
LL ans=0;
inline LL read()
{
    LL kr=1,xs=0;
    char ls;
    ls=getchar();
    while(!isdigit(ls))
    {
        if(!(ls^45))
            kr=-1;
        ls=getchar();
    }
    while(isdigit(ls))
    {
        xs=(xs<<1)+(xs<<3)+(ls^48);
        ls=getchar();
    }
    return xs*kr;
}
inline void ready()
{
    for(LL i=1;i<=n;i++)
        for(LL j=2;j<=n/i;j++)
        {
            if(i*j>n) break;
            sum[i*j]+=i;
        }
}
inline void dp()
{
    for(LL i=n;i>=1;i--)//因为大数字一定是小数字的后代(逆序) 
        if(sum[i]<i)//表示i为sum[i]的子节点 
        {
            if(d1[i]+1>d1[sum[i]])//修改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();
    ready();
    dp();
    for(LL i=1;i<=n;i++)
        ans=max(ans,d1[i]+d2[i]);//更新答案 
    printf("%lld\n",ans);
return 0;
}

 

posted @ 2018-10-10 22:28  落笔映惆怅丶  阅读(343)  评论(0)    收藏  举报