[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;
}

浙公网安备 33010602011771号