如何对一个问题挖掘信息把它变成已知的问题十分重要,这一题恰恰体现这一点:
https://www.acwing.com/problem/content/1077/
首先,对于一个数x,它对应一个其约数之和y,同时他们可以相互转换,于是我们可以给他们连一条边。
这样子,1--n的数就抽象成图中的点,我们进一步看看约数之和<自己的条件,这就避免了出现回路:假如有A,B,C构成一个回路,那么令A的约数之和为B,B的约数之和为C,那么A只能是C的约数之和,于是A>B>C>A,矛盾
于是整个图就可以抽象成森林。问题就是求每一个树的最大转换次数,也就是求最长链这一经典问题。
考虑n<50000,直接暴力枚举肯定TLE,我们可以参考毕业季那题从因子出发枚举它倍数即可(nlogn)
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[500010];
int vis[500010];
int ans=0;
int res=0;
vector<int> edge[500010];
int dfs(int root,int fa){vis[root]=1;int dist = 0; // 表示从当前点往下走的最大长度int d1 = 0, d2 = 0;for (int i = 0; i<edge[root].size(); i++){int j = edge[root][i];if (j == fa) continue;int d = dfs(j, root) + 1;dist = max(dist, d);if (d >= d1) d2 = d1, d1 = d;else if (d > d2) d2 = d;}ans = max(ans, d1 + d2);return dist;
}
int main(){cin>>n;for(int i=1;i<=n;i++){for(int k=2;k*i<=n;k++){a[k*i]+=i;}}for(int i=2;i<=n;i++){if(a[i]>=i) continue;edge[i].push_back(a[i]);edge[a[i]].push_back(i);}for(int i=2;i<=n;i++){if(vis[i]) continue;ans=0;dfs(i,-1);res=max(res,ans);}cout<<res;
}