题目描述
一个无向图被称为“虫图”,当且仅当:①它是一棵树;②存在某条树中的简单路径 p,满足树中任何一个点到 p 的最短距离不超过 1。现在给出一个 n 个结点 m 条边的无向图 G,没有重边,可能有自环。定义一次合并操作是:对于两个不同的结点 a 和 b,将它们合并成新结点 w,原图中如果存在边(x, a) 或者(x,b),那么新图中就存在边(x, w) 。问最少要用多少次合并操作,使得该无向图变为“虫图”。输入格式
第1行:2个整数n(1≤n≤5000)和m(1≤m≤10^5),表示图的结点数和边数接下来m行,每行2个整数a 和 b,表示一条边
输出格式
第1行:1个整数,表示答案
输入样例
4 4
1 2
2 3
3 4
4 2
输出样例
2
题解
虫图的定义为一棵树,而树没有没有环,所以要将原图所有的环缩成一个点。缩点时记录操作数。
很容易知道路径p就是树的直径。对于直径上的任意一个结点x,都有一个以x为根的子树,对这棵子树进行合并操作,使得没有点的深度超过 1,合并时记录操作数。
最后将所有的树连接, 操作数为树的棵数-1次。
也可以直接算出最后虫树的总点数,再用n去减。下列代码是后者。
#include<cstring> #include<cstdio> #include<algorithm> using namespace std; const int N=5005; const int M=200005; void Getin( int &shu ) { char c; int f=1; shu=0; for( c=getchar(); c<'0' || c>'9'; c=getchar() ) if( c=='-' ) f=-1; for( ; c>='0' && c<='9'; c=getchar() ) shu=shu*10+c-'0'; shu*=f; } int fir[N], ecnt=1; struct enode{ int e, next; } edge[M]; void Elink( int s, int e ) { edge[++ecnt].e=e; edge[ecnt].next=fir[s]; fir[s]=ecnt; edge[++ecnt].e=s; edge[ecnt].next=fir[e]; fir[e]=ecnt; } int n, m, s, e, cnt; void Build_map() { Getin(n); Getin(m); for( int i=1; i<=m; i++ ) Getin(s), Getin(e), Elink( s, e ); } int dfn[N], low[N], dfs_clock, pcnt; bool bri[M]; void Tarjan( int s, int last ) { dfn[s]=low[s]=++dfs_clock; for( int i=fir[s]; i; i=edge[i].next ) if( !dfn[ edge[i].e ] ) { Tarjan( edge[i].e, i ); low[s]=min( low[s], low[ edge[i].e ] ); if( dfn[s]<low[ edge[i].e ] ) bri[i]=bri[i^1]=1; } else if( i!=(last^1) ) low[s]=min( low[s], dfn[ edge[i].e ] ); } bool vis[N], map[N][N]; void Build( int s, int col ) { low[s]=col; vis[s]=1; for( int i=fir[s]; i; i=edge[i].next ) if( !vis[ edge[i].e ] ) { if( bri[i] ) Build( edge[i].e, ++pcnt ); else Build( edge[i].e, col ); } } int pot[N], tcnt; struct tnode{ int e, next; } tree[M]; void Tlink( int s, int e ) { tree[++tcnt].e=e; tree[tcnt].next=pot[s]; pot[s]=tcnt; } int rot[N], rcnt; void Build_tree() { for( int i=1; i<=n; i++ ) if( !dfn[i] ) rot[++rcnt]=i, Tarjan( i, -1 ); for( int i=1; i<=rcnt; i++ ) Build( rot[i], ++pcnt ); for( int s=1; s<=n; s++ ) for( int i=fir[s]; i; i=edge[i].next ) if( low[s]!=low[ edge[i].e ] ) if( !map[ low[s] ][ low[ edge[i].e ] ] ) { Tlink( low[s], low[ edge[i].e ] ); map[ low[s] ][ low[ edge[i].e ] ]=1; } } int ans, sum, fa[N]; void DFS( int s, int f, int d, int &p ) { if( ans<d ) ans=d, p=s; for( int i=pot[s]; i; i=tree[i].next ) if( tree[i].e!=f ) { DFS( tree[i].e, s, d+1, p ); fa[ tree[i].e ]=s; } } void Ansp( int s, int f ) { sum++; for( int i=pot[s]; i; i=tree[i].next ) if( tree[i].e!=f ) { if( vis[ tree[i].e ] ) Ansp( tree[i].e, s ); else sum++; } } void Find_D() { memset( vis, 0, sizeof vis ); for( int i=1; i<=rcnt; i++ ) { ans=0; DFS( low[ rot[i] ], -1, 1, s ); ans=0; fa[s]=0; DFS( s, -1, 1, e ); vis[ ans=e ]=1; while( fa[ ans ] ) vis[ fa[ ans ] ]=1, ans=fa[ans]; Ansp( low[ rot[i] ], -1 ); } sum-=(rcnt-1); } int main() { Build_map(); Build_tree(); Find_D(); printf( "%d\n", n-sum ); return 0; }