题解 【P3174 [HAOI2009]毛毛虫】
\(\large\texttt{Read in my Blog}\)
做法:树形 \(DP\)
题目中的 \(M\) 到底有什么用啊QwQ
\(\Large\texttt{Meaning}\)
题目“抽”字我一开始看还是有点迷惑的,其实实意就是在一棵树中取一条链,求链上的点和与它们相邻的点的个数最大。
\(\Large\texttt{Solution}\)
考虑每一个点 \(n\) ,若这条链经过点 \(n\) ,那么一定是从 \(n\) 的一棵子树的叶子开始,到另一棵子树的叶子,显然这肯定是最大的。
这样,我们就要记录每个点两个值:与它相邻的节点个数(包括它,不包括父节点);从这个节点到它子树的某个叶子节点,这条链上有贡献的节点个数(不包括父节点),令第一个记录值为数组 \(s\) ,第二个记录值数组 \(h\) 。
\(s[n]\) 很好求, \(h[n]\) 即为 \(s[n]-1+\max_{v\in n} h[v]\)(因为 \(s[n]\) 和 \(h[v]\) 都包括了 \(v\) 这个节点),但是注意若 \(n\) 事叶子节点,不用减一。
然后就可以求我们想要求的链了,记录子节点中最大和次大的的 \(h\) 值, 加上这个节点的相邻节点个数( \(s[n]\) )就好了,但要注意判重,和细节(见代码)。
\(\Large\texttt{Code}\)
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <map>
#include <iostream>
#include <iterator>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <vector>
// #include <bits/stdc++.h>
using namespace std;
// #define ls now << 1
// #define rs now << 1 | 1
#define PB push_back
// #define MP make_pair
// #define int long long
// #define us unsigned
// #define LL long long
const int N = 300000;
// const int M = 1e3;
// #define re register
// const int mod = 1e;
// const int inf = 0x7fffffff;
// const double inf_double = 1e4;
// const double eps = 1e-4;
int a, siz[N + 10], ans, dp[N + 10], s[N + 10], h[N + 10];
vector<int> st[N + 10];
inline void dfs(int n, int fa)
{
s[n] = 1;
h[n] = 1;
int mx1 = 0, mx2 = 0;
for (int i = 0; i < st[n].size(); i++)
{
int v = st[n][i];
if (v == fa)
continue;
dfs(v, n);
s[n]++;
h[n] = max(h[n], h[v]);
if (h[v] >= mx1)
{
mx2 = mx1;//顺序不要错
mx1 = h[v];
}
else if (h[v] > mx2)
mx2 = h[v];
}
h[n] += s[n];
if (st[n].size())//不是叶子节点的时候
h[n]--;
int tmp = mx1 + mx2 + s[n] - (mx1 != 0) - (mx2 != 0) + (fa != 0);//注意孩子节点的个数少于2的时候,和没有父节点的时候。
ans = max(ans, tmp);//记录当前最大值
}
signed main()
{
scanf("%d%*d", &a);
int x, y;
for (int i = 1; i < a; i++)
{
scanf("%d%d", &x, &y);
st[x].PB(y);
st[y].PB(x);
}
dfs(1, 0);
printf("%d", ans);
return 0;
}