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;
}
posted @ 2019-12-27 16:55  aigsy  阅读(265)  评论(0编辑  收藏  举报