cf 51F Caterpillar (双联通分量 树的直径)
题意:给一个图,要求你不断合并两点,使得图变为一棵树,形态是有一条主链,其余的点到这条主链的距离最大为1并且只有一条边与其连接。
首先把双联通分量缩点,因为只能一条边与主链连接,容易发现一个大小为\(x\)的双联通分量需要\(x-1\)次缩点。这样完了以后得到一个森林。
然后就对于这个森林里的每棵树,找到直径以后,把直径作为主链,然后叶子可以不管;但是既不属于直径又不是叶子的点需要合并到其他点上去。
最后把所有的树合成一棵树就行了
#include <bits/stdc++.h>
using namespace std;
inline int readInt() {
char c; int tmp=0,x=1; c=getchar();
while(c>'9' || c<'0') {if(c=='-') x=-1; c=getchar();}
while(c>='0' && c<='9') {tmp=tmp*10+c-'0'; c=getchar();}
return tmp*x;
}
const int maxN=2000+10;
const int maxM=100000+10;
int Head[maxN],eg[maxM<<1],nxt[maxM<<1],tot=1;
void addEdge(int u,int v) {
eg[++tot]=v; nxt[tot]=Head[u]; Head[u]=tot;
eg[++tot]=u; nxt[tot]=Head[v]; Head[v]=tot;
}
bool isbri[maxM<<2];
int dfn[maxN],low[maxN],dfsclock=0;
void tarjan(int v,int InEdge) {
dfn[v]=low[v]=++dfsclock;
for(int i=Head[v];i;i=nxt[i]) {
int u=eg[i];
if(!dfn[u]) {
tarjan(u,i);
low[v]=min(low[v],low[u]);
if(low[u]>dfn[v]) isbri[i]=isbri[i^1]=true;
}
else if(i!=(InEdge^1)) low[v]=min(low[v],dfn[u]);
}
}
int bccno[maxN],num=0,bccsiz[maxN];
void dfs(int v) {
bccno[v]=num; bccsiz[num]++;
for(int i=Head[v];i;i=nxt[i]) {
int u=eg[i];
if(!bccno[u] && !isbri[i]) dfs(u);
}
}
vector<int > g[maxN];
void new_addEdge(int u,int v) {
g[u].push_back(v); g[v].push_back(u);
}
bool vis[maxN],isLeaf[maxN];
int tmp=0,tmpmax=-1,tmpleaf=0,Leaf[maxN],siz[maxN],rot[maxN],cnt=0,Fa[maxN],Belong[maxN];
void predfs(int v,int fa) {
siz[v]=1; Fa[v]=fa; Belong[v]=cnt;
if((int)g[v].size()==0 || (int)g[v].size()==1) Leaf[cnt]++,isLeaf[v]=true;
for(int i=0;i<(int)g[v].size();i++) {
int u=g[v][i];
if(u!=fa) predfs(u,v),siz[v]+=siz[u];
}
}
void FindLongest(int v,int fa,int D,int lev) {
if(tmp<D) tmp=D,tmpmax=v,tmpleaf=lev;
for(int i=0;i<(int)g[v].size();i++) {
int u=g[v][i];
if(u!=fa) FindLongest(u,v,D+(!isLeaf[u]),lev+isLeaf[u]);
}
}
int FindDiameter(int v) {
tmp=0,tmpmax=-1;
for(int i=0;i<(int)g[v].size();i++) {
int u=g[v][i];
FindLongest(u,v,1,0);
}
if(!(~tmpmax)) return 0;
int vv=tmpmax;
tmp=0,tmpmax=-1,tmpleaf=0;
FindLongest(vv,-1,1,isLeaf[vv]);
return tmp-tmpleaf;
}
int n,m;
int main() {
n=readInt(),m=readInt();
int u,v;
for(int i=1;i<=m;i++) {
u=readInt(),v=readInt();
addEdge(u,v);
}
for(int i=1;i<=n;i++) {
if(!dfn[i]) tarjan(i,0);
}
for(int i=1;i<=n;i++) {
if(!bccno[i]) {
num++;
dfs(i);
}
}
for(int i=2;i<=tot;i+=2) {
if(bccno[eg[i]]!=bccno[eg[i^1]]) {
new_addEdge(bccno[eg[i]],bccno[eg[i^1]]);
}
}
int ans=0;
for(int i=1;i<=num;i++) ans+=bccsiz[i]-1;
cnt=0;
for(int i=1;i<=num;i++) {
if(!Belong[i]) cnt++,rot[cnt]=i,predfs(i,-1);
}
ans+=cnt-1;
for(int i=1;i<=cnt;i++) {
int d=FindDiameter(rot[i]);
ans+=siz[rot[i]]-d-Leaf[i];
}
printf("%d\n",ans);
return 0;
}