題目描述
一個無向圖被稱爲“蟲圖”,當且僅當:①它是一棵樹;②存在某條樹中的簡單路徑 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 44 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; }
[NOIP模擬賽]蟲圖
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.