[NOIP模擬賽]蟲圖

題目描述
一個無向圖被稱爲“蟲圖”,當且僅當:①它是一棵樹;②存在某條樹中的簡單路徑 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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章