题解:P8028 [COCI 2021/2022 #3] Cijanobakterije
树的直径类模板题。
题目大意
给你一个森林,你可以连若干条边,但是连边之后整个图仍然是一个森林。问你连完后的最大直径。
题目中的“直径”意思是连接的点的个数。
解题思路
为了使得能连接的点的个数最多,我们肯定要把每棵树的直径连在一起,这样就能使得答案最大。
所以,我们只需要对于每棵树求一遍直径,然后把答案累加起来就能够 AC 了。
树的直径还是简单提以下吧。可以使用树形 DP 求解。我们设 \(dp_{i,0}\) 为从 \(i\) 号点出发的最长链,\(dp_{i,1}\) 表示次长链(但是与最长链没有重复部分)。如果 \(dp_{son,0}+1>dp_{i,0}\),那么就能够更新最长链,把次长链更新成原来的最长链即可。如果 \(dp_{son,0}+1>dp_{i,1}\),就直接更新次长链。
AC 代码:
#include <iostream>
#include <vector>
#define int long long
using namespace std;
bool vis[100005];
vector<int> g[100005];
int dp[100005][2];
int n,m;
int nowans = 0;
void dfs(int now,int fa){
vis[now] = true;
dp[now][0] = dp[now][1] = 1;
for(auto it:g[now]){
if(it==fa) continue;
dfs(it,now);
if(dp[it][0]+1>dp[now][0]){
dp[now][1] = dp[now][0];
dp[now][0] = dp[it][0]+1;
}else if(dp[it][0]+1>dp[now][1]){
dp[now][1] = dp[it][0]+1;
}
}
nowans = max(nowans,dp[now][0]+dp[now][1]-1);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i = 1;i<=m;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
int fans = 0;
for(int i = 1;i<=n;i++){
if(!vis[i]){//计算每棵树的直径,加和
dfs(i,-114514);
fans+=nowans;
nowans = 0;
}
}
cout<<fans;
return 0;
}