CF51F - Caterpillar 题解
chenxia25
2021-08-05 09:32:05
不能有重边这个条件很有趣啊。就是认为重边也算环,而且把重边定义成环确实是最合理的。那么对于每个环,你只要不把这个环上所有点并成一个点,这个环就永远存在。所以需要先 eDCC 缩点(把每个 DCC 合并成一个点),得到一个森林。
下面考虑对任意一棵树,怎么用最少的合并次数合并出一个毛毛虫。考虑最终毛毛虫的一个主干路径,那么将这个路径还原之后长度一定是不变的,也就是说不会通过合并路径上的点来造主干路径,因为这不起丝毫作用。
那现在钦定一条路径,看怎么能把所有点都压到离路径只有 $1$ 距离。手玩一波知道最优方案是把所有不在路径上的叶子保留下来,其它点全部坍塌到路径上。这样操作次数是 $n-(len+leaf-2)$,其中 $len$ 是钦定路径长度,$leaf$ 是叶子个数。只有 $len$ 是变量,找直径就好了(bzd 为啥这题 $n$ 只有 $2000$?正好我可以抄之前写过的 eDCC 缩点 + LCA 的板子暴力枚举路径,平方对数())。
对多个连通分量(森林里的多个树),只需要两两合并即可,操作次数 $c-1$,其中 $c$ 是树的棵树。
```cpp
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=200010,LOG_N=20;
int n,m,qu;
vector<int> nei[N],rv[N];
vector<bool> brg[N];
int dfn[N],low[N],nowdfn;
void tar(int x=1,int fa=0){//to modify
dfn[x]=low[x]=++nowdfn;
for(int i=0;i<nei[x].size();i++){
int y=nei[x][i];
if(y==fa){fa=-1;continue;}
if(!dfn[y]){
tar(y,x),low[x]=min(low[x],low[y]);
if(dfn[x]<low[y])brg[x][i]=brg[y][rv[x][i]]=true;
}
else low[x]=min(low[x],dfn[y]);
}
}
int cnt;
int cid[N];
void dfs(int x=1){
cid[x]=cnt;
for(int i=0;i<nei[x].size();i++)if(!brg[x][i]){
int y=nei[x][i];
if(!cid[y])dfs(y);
}
}
vector<int> cnei[N];
int fa[N][LOG_N],dep[N];
vector<int> cc;
void dfs0(int x){
cc.pb(x);
for(int i=1;i<LOG_N;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=0;i<cnei[x].size();i++){
int y=cnei[x][i];
if(y==fa[x][0])continue;
fa[y][0]=x;
dep[y]=dep[x]+1;
dfs0(y);
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=LOG_N-1;~i;i--)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=LOG_N-1;~i;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main(){
cin>>n>>m;
while(m--){
int x,y;
scanf("%d%d",&x,&y);
nei[x].pb(y),nei[y].pb(x);
rv[x].pb(nei[y].size()-1),rv[y].pb(nei[x].size()-1);
brg[x].pb(false),brg[y].pb(false);
}
for(int i=1;i<=n;i++)if(!dfn[i])tar(i);
for(int i=1;i<=n;i++)if(!cid[i])cnt++,dfs(i);
for(int i=1;i<=n;i++)for(int j=0;j<nei[i].size();j++){
int x=nei[i][j];
if(cid[i]!=cid[x])cnei[cid[i]].pb(cid[x]);
}
for(int i=1;i<=cnt;i++){
vector<int> &v=cnei[i];
sort(v.begin(),v.end());
v.resize(unique(v.begin(),v.end())-v.begin());
}
int final=-1;
for(int i=1;i<=n;i++)if(!dep[i]){
dep[i]=1;
cc.clear();
dfs0(i);
if(cc.size()==1)continue;
int ans=0,leaf=0;
for(int j=0;j<cc.size();j++)leaf+=cnei[cc[j]].size()==1;
for(int ii=0;ii<cc.size();ii++)for(int jj=0;jj<cc.size();jj++){
int i=cc[ii],j=cc[jj],k=lca(i,j);
if(cnei[i].size()==1&cnei[j].size()==1&&i!=j)ans=max(ans,dep[i]+dep[j]-dep[k]-dep[fa[k][0]]+(leaf-2));
}
final+=-ans+1;
}
cout<<n+final;
return 0;
}
```