[CF51F] Caterpillar 题解

首先对于不是一个联通块的点,设联通块个数为 \(c\),则我们在最后需要通过 \(c-1\) 次操作使其联通。

我们势必是要化边双为点的,所以我们跑一次边双连通分量。设一共有 \(k\) 个边双连通分量,则我们需要进行 \(n-k\) 次操作去掉所有边双连通分量。剩下的就是一棵树了。

考虑每一次合并操作可以让一条链长度 \(-1\),所以除了树的叶子节点以外,不在最终链上的点都得被合并一次。最终链显然是树的直径,不过要注意直径两端的叶子节点。

时间复杂度 \(O(n+m)\)

#include<bits/stdc++.h>
using namespace std;
const int N=2005,M=1e5+5;
int n,m,id,idx[N],cnt,ans,dep[N];
int dfn[N],low[N],vis[N],st[N],tp;
vector<int>g[N],ve[N];int u[M],v[M];
void tarjan(int x,int fa){
	dfn[x]=low[x]=++id,vis[x]=1,st[++tp]=x;
	for(auto y:g[x]){
		if(!dfn[y]) tarjan(y,x),low[x]=min(low[x],low[y]);
		else if(vis[y]&&y!=fa) low[x]=min(low[x],dfn[y]);
	}if(dfn[x]!=low[x]) return;cnt++,ans--;
	while(st[tp+1]!=x)
		vis[st[tp]]=0,idx[st[tp--]]=cnt,ans++;
}void dfs(int x,int fa,int &rt,int it){
	dep[x]=dep[fa]+1;
	for(auto y:ve[x])
		if(y!=fa) dfs(y,x,rt,it);
	if(ve[x].size()!=1) ans+=it;
	if(dep[x]>dep[rt]) rt=x;
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m,ans=-1;
	for(int i=1;i<=m;i++){
		cin>>u[i]>>v[i];
		g[u[i]].push_back(v[i]);
		g[v[i]].push_back(u[i]);
	}for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i,0);
	for(int i=1;i<=m;i++){
		if(idx[u[i]]==idx[v[i]]) continue;
		ve[idx[u[i]]].push_back(idx[v[i]]);
		ve[idx[v[i]]].push_back(idx[u[i]]);
	}for(int i=1;i<=cnt;i++){
		if(dep[i]) continue;
		ans++;int x=0,y=0;
		if(!ve[i].size()) continue;
		dfs(i,0,x,1),dfs(x,0,y,0);
		ans-=dep[y]-2;
	}cout<<ans;
	return 0;
} 
posted @ 2025-03-11 10:47  white_tiger  阅读(36)  评论(0)    收藏  举报