P3174 [HAOI2009]毛毛虫 题解

CSDN同步

原题链接

简要题意:

给定一棵树,求最长的 “挂链” 长度。

挂链定义为:一条链上所有节点与其相连的节点构成的生成树。(非严谨定义)(原题中是 “毛毛虫”,本人以为挂链更形象)

这题有多种做法,这里给出思路,以及其中一种做法的代码。

算法一

注意到,其实我们只需要选出 “最长链”,然后在最长链的两侧挂链即可。

即,先求出 树的直径 的两个端点,然后遍历一遍直径上的端点,把它们的直接连边都加入生成树中。

最后统计答案即可。

时间复杂度:\(O(n)\).

期望得分:\(100pts\).

算法二

考虑树形 \(\text{dp}\),用 \(f_i\) 表示以 \(i\) 为根的最大挂链长度,\(sub_i\) 记录 \(i\) 的儿子个数,则:

\[f_u = \max(f_v , f_v + sub_u - 1) (v \in \operatorname{son} (u))
\]

很显然,要么直接继承儿子节点的答案,要么把两边一拼。

时间复杂度:\(O(n)\).

期望得分:\(100pts\).

算法三

给出一个最巧妙的算法。

假设 \(i\) 号点的度是 \(a_i\),则一条长度为 \(s\) 的挂链的答案应该是:

\[\bigg ( \sum_{i \in \operatorname{list}} a_i \bigg ) -(s-1) + 1
\]

\(i \in \operatorname{list}\) 表示 \(i\) 属于当前链,\(s-1\) 即把重复计算的边去掉得到边数,然后 \(+1\) 得到点数。化简即:

\[= \bigg ( \sum_{i \in \operatorname{list} } a_i \bigg ) - s + 2
\]

你发现这个东西有点难看,于是换了一下:

\[=\sum_{i \in \operatorname{list} } (a_i - 1) + 2
\]

显然,把 \(s\) 减到每个节点里面 ,每个点分 \(1\) 个。

那么我们可以按照如下步骤求解:

  1. 建图统计度,并将度 \(-1\).
  2. 求出直径两端。
  3. 统计直径上所有节点的度,然后 \(+2\) 即为答案。

时间复杂度:\(O(n)\).

实际得分:\(100pts\).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=3e5+1;

inline int read(){char ch=getchar();int f=1; while(!isdigit(ch)) {if(ch==‘-‘) f=-f; ch=getchar();}
	   int x=0;while(isdigit(ch)) x=x*10+ch-‘0‘,ch=getchar(); return x*f;}

int n,m,du[N],dis[N];
int num,mx;
vector<int> G[N];

inline void dfs(int dep,int fa,int dis) {
//fa 是 dep 的父亲 , dep 是正在搜索的节点,dis 是它与开始搜索节点的距离,dis 统计度和
	if(dis>mx) mx=dis,num=dep; //度和打擂
	for(int i=0;i<G[dep].size();i++)
		if(G[dep][i]!=fa) dfs(G[dep][i],dep,dis+du[dep]); //往下搜索
}

int main(){
	n=read(),m=read();
	memset(du,-1,sizeof(du)); //初始化为 -1, 即减掉 1
	while(m--) {
		int x=read(),y=read();
		G[x].push_back(y);
		G[y].push_back(x);
		du[x]++; du[y]++; //统计度
	} dfs(1,0,du[1]); mx=0;
	dfs(num,0,du[num]); //两次 dfs 寻找直径
	printf("%d\n",mx+2); // +2 就是答案
	return 0;
}

原文地址:https://www.cnblogs.com/bifanwen/p/12642080.html

时间: 2024-10-20 11:45:09

P3174 [HAOI2009]毛毛虫 题解的相关文章

P3174 [HAOI2009]毛毛虫

题目描述 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图 1 )抽出一部分就变成了右边的一个毛毛虫了(图 2 ). 输入输出格式 输入格式: 在文本文件 worm.in 中第一行两个整数 N , M ,分别表示树中结点个数和树的边数. 接下来 M 行,每行两个整数 a, b 表示点 a 和点 b 有边连接( a, b ≤ N ).你可以假定没有一对相同的 (a, b) 会出现一次以上. 输出格式: 在文本文件 worm.o

毛毛虫题解

毛毛虫题解 此题倒是简单. 一个毛毛虫可以看做一层层的儿子节点相加(加上自己与父亲). 我们枚举每个点作为连接两条链的交点,更新\(ans\), 直接上代码吧: #include<bits/stdc++.h> using namespace std; const int N=3e5+7; int n,m,t1,t2,cnt=0,ans=0,f[N],head[N]; struct edge{int nxt,to;}e[N<<1]; map<int,int> ha[N];

题解——[HAOI2009]毛毛虫 树形DP

题意: 给你一棵树,从树中取出一部分满足:是一条链+一些直接连在这条链上的节点 求节点数最多的合法取出部分. 题解: 其实这题还是不难? 观察到对于任意一条链, 只有两种情况: 一条路走到底 or  以某个点为中转 f[x]表示从x往下走,一路走到底的包括x的最优解, f[x]包括x也包括father[x](将会加入它的贡献) 观察到以某个点为中转的情况: 倘若某条链以一个点为中转,那么这条链将无法向上产生贡献, 若没有,则变为第一种情况,且一定可以向上产生贡献, 以点x为中转的所有链都可以通过

【Luogu P3174 】[HAOI2009]毛毛虫

前言: 虽然很多人和我想法一样 ,但我还是不要脸地写了这题解 题目: 链接 大意: 在一棵树上取一条最长链以及它所连接的结点总共的结点个数 思路: 取链: 用树形\(DP\)就可以轻而易举的解决这个问题: \(f_x\)表示以\(x\)为根节点的树的深度 转移方程: \[f_x=max\{f_y + 1 \} (y\in son(x))\] 那么以\(x\)为根节点的树的最长链就是\(f_x\)加上次大的子树深度,下方代码区以\(ans\)来表示. 代码: void dp(int x, int

[haoi2009]毛毛虫 树形dp

这道题细节处理不少,但要AC不难: 设以i节点为根节点的子树能形成的最大的毛毛虫长度为f[i],则f[i]=max(f[j])+i节点的孩子数: 答案需要f最大和次大的两个子树合并,而且若合并的位置不是根节点,ans++: 我就是坑在了最后一点上,最后打表找到了问题: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn=30

[HAOI2009]毛毛虫

题目描述 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图 1 )抽出一部分就变成了右边的一个毛毛虫了(图 2 ). 输入输出格式 输入格式: 在文本文件 worm.in 中第一行两个整数 N , M ,分别表示树中结点个数和树的边数. 接下来 M 行,每行两个整数 a, b 表示点 a 和点 b 有边连接( a, b ≤ N ).你可以假定没有一对相同的 (a, b) 会出现一次以上. 输出格式: 在文本文件 worm.o

[HAOI2009] 毛毛虫 - 树的直径

在一棵树中,一条链及与它直接相连的所有边的集合称作一个毛毛虫,这个子图中的点数称作这个毛毛虫的大小.求一棵树中最大的毛毛虫.\(N\leq 3\times10^5\) Solution 设每个点的权值为 \(deg_i-1\),然后求最长路即可,答案就是 \(ans+2\) #include <bits/stdc++.h> using namespace std; const int N = 300005; vector <int> g[N]; int n,m,vis[N],a[N

【HAOI2009】【毛毛虫】【树形dp】

试题描述 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图 1 )抽出一部分就变成了右边的一个毛毛虫了(图 2 ). 输入数据 在文本文件 worm.in 中第一行两个整数 N , M ,分别表示树中结点个数和树的边数. 接下来 M 行,每行两个整数 a, b 表示点 a 和点 b 有边连接( a, b ≤ N ).你可以假定没有一对相同的 (a, b) 会出现一次以上. 输出数据 在文本文件 worm.out 中写入一个整

【HAOI2009】【P1307】毛毛虫

感觉相比其他树归题简单多了,不过有点绕(也许是我的思路很奇怪一.一)(这是省选题啊,就算作为T1这题也太水了,HA好弱……) 原题: 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图1)抽出一部分就变成了右边的一个毛毛虫了(图2). N≤300000 先搞出无根树,这回不枚举中间点了,说说我的奇怪的做法一.一 搞两个数组,一个是uf,表示包括自己在内的直系最大值,另一个是bf,表示x和x往下的兄弟中uf最大的一个 然后就是求