1577:【例 3】数字转换
题目
因为一个x只可以变成一个y(对应着树中每个节点只有一个父亲), 且 y < x ( x 必与之前的点相连 ),所以这些( x , y )构成了一棵树
求最多的变换步数,每一条边代表一次变换,所以问题就是求一颗树的直径
ps.求约数和可采用倍数法
求树的直径
设 d[x] 为从以根节点x出发能达到的最远距离:
\[d[x]=max(d[y]+edge(x,y),d[x])
\]
为以x为根节点的树的直径,对于任意两个子节点yj,yi,则f[x]由4部分组成:
\[f[x]=d[y_j]+c(x,y_j)+d[y_i]+c(x,y_i)
\]
但在实际操作中,不需要用两层循环枚举 i 和 j ,在 d[x] 中已经保存了 从 x 走 向以 yj 为根子树的最远距离,所以先用 d[yi] + c(x,yi) 更新 f[x] ,再用 d[yi] + c(x,yi) 更新 d[x] 即可
另外 , f[x] 可以用 变量 ans 代替 , 在自下而上回溯过程中更新
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=500010;
vector<int> factor[500010];
int sum[MAXN],ans,N;
int h[MAXN],cnt,d[MAXN];
struct node{
int next,to,val;
}edg[MAXN*2];
void add(int x,int y,int z) {
edg[++cnt].next=h[x];
edg[cnt].to=y;
edg[cnt].val=z;
h[x]=cnt;
}
bool v[MAXN];
void dp(int x) {
d[x]=0;
v[x]=1;
for(int i=h[x];i;i=edg[i].next) {
int y=edg[i].to,z=edg[i].val;
if(v[y]) continue;
dp(y);
ans=max(ans,d[x]+d[y]+z);
d[x]=max(d[x],d[y]+z);
}
}
int main() {
scanf("%d",&N);
//倍数法求因数
for(int i=1;i<=N;++i)
for(int j=2;j<=N/i;j++)
sum[i*j]+=i;
for(int i=2;i<=N;++i) {
if(sum[i] >= i) continue;
add(i,sum[i],1);
add(sum[i],i,1);
}
dp(1);
printf("%d",ans);
return 0;
}