P8028 [COCI2021-2022#3] Cijanobakterije

简要题意

给定一个森林,可以在树与树之间连边,不可成环,求最长链。

解析

对于一棵树而言,她本身的最长链自然就是她的直径。

贪心的想,把所有的树的直径全部连接起来就是最长的那一条链。

所以,对于每一棵树求出她的直径然后求和就好了。

特别注意,这题的长度是指节点的个数,因此比边数大 11

代码实现

树的直径有两种求法:两遍搜索和树形 DP。

前者可以保存遍历路径但无法处理负边权,后者则正好反之。

搜索

先选树上任意一点 uu,然后找到距离 uu 最远的点 vv,之后再以 vv 为起点,找到离她最远的点 ww。最后 vvww 之间的距离即为该树的直径。

理论上 DFS 与 BFS 均可,但写 DFS 的似乎更多。

DP

别的题解讲的还挺明白的,而且本题也没有负边权我不会,这里就不放了。

此外,树的直径还有不少性质,这里贴个链接,如需自取。

终于来到了喜闻乐见的代码环节。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+12;
int n,m;
int head[maxn],cnt=0;
struct node {
	int nxt,to;
} edge[maxn];
bool vis[maxn],vis2[maxn];
int ans=0,o=0,dis=0;
void add(int u,int v) {
	cnt++;
	edge[cnt].to=v;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
void dfs1(int u,int d) {
	vis[u]=1;
	if(d+1>dis) {
		dis=d+1;
		o=u;
	}
	for(int i=head[u]; i; i=edge[i].nxt) {
		int v=edge[i].to;
		if(!vis[v])
			dfs1(v,d+1);
	}
}
void dfs2(int u,int d) {//可以跟dfs1和到一起的,不过容易出锅,模拟赛100->30。
	vis2[u]=1;
	dis=max(dis,d+1);
	for(int i=head[u]; i; i=edge[i].nxt) {
		int v=edge[i].to;
		if(!vis2[v]) {
			dfs2(v,d+1);
		}
	}
}
int main() {
	cin>>n>>m;
	for(int i=1; i<=m; i++) {
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	for(int i=1; i<=n; i++) {
		if(!vis[i]) {
			dis=0;//记得清零 
			dfs1(i,0);
			dfs2(o,0);
			ans+=dis;
		}
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2023-11-08 20:46  p7gab  阅读(0)  评论(0编辑  收藏  举报  来源