CF51F. Caterpilla 题解

首先,算出所有的双联通分量。
一个双联通分量里的点肯定都要缩成一个点,不然存在环。
答案加上 n − 双 联 通 分 量 的 个 数 n-双联通分量的个数 n
缩完点的图肯定不含有环,也就是“森林”。
问题就转化成在一棵树上找到一个最优的缩点方式,然后将每一棵树的链首尾相连就行了。
可以发现对于一颗树找一条链,消去非叶子且不在换上的节点。所以链要尽可能长,也就是直径。
Code:

/*
AuThOr Gwj
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
const int MAXN=2000+20; 
int n,m,belong[MAXN],val[MAXN],sum[MAXN],cntBcc,depth[MAXN],low[MAXN];
bool vis[MAXN];
vector<int> g[MAXN];
stack<int> s;
void dfs(int now,int pre,int deep=1){
	s.push(now);
	depth[now]=deep;
	vis[now]=1;
	low[now]=deep;
	for(auto it:g[now]){
		if(it!=pre){
			if(!vis[it]){
				dfs(it,now,deep+1); 
				low[now]=min(low[now],low[it]);
			}
			else{
				low[now]=min(low[now],depth[it]);
			}
		}
	}
	if(low[now]==deep){
		cntBcc++;
//		cout<<cntBcc<<"!".<<endl;
		while(!s.empty()){
			int index=s.top();
//			cout<<index<<endl;
			s.pop();
			val[cntBcc]++;
			belong[index]=cntBcc;
			if(index==now) break;
		}
	}
}
int res,cntCc=0,bestValue[MAXN],cntLeaves[MAXN];
vector<int> CC[MAXN];
bool calc[MAXN];
int dp[MAXN];
int ccSize[MAXN];
void dfsCheck(int now,int pre){
	vis[now]=1;
	ccSize[cntCc]++;
	CC[cntCc].PB(now);
	for(auto it:g[now]){
		if(it!=pre){
			dfsCheck(it,now);
		}
	}
}
void dfsInit(int now,int pre){
	sum[now]=1;
	cntLeaves[now]=0;
	int cntChild=0;
	for(auto it:g[now]){
		if(it!=pre){
			cntChild++;
			dfsInit(it,now);
			sum[now]+=sum[it];
			cntLeaves[now]+=cntLeaves[it]; 
		}
	}
	if(!cntChild)
		cntLeaves[now]=1;
}
int dfsDp(int now,int pre){
	if(calc[now]) return dp[now];
	calc[now]=1;
	dp[now]=1;
	for(auto it:g[now]){
		if(it!=pre)
		dp[now]=max(dp[now],dfsDp(it,now)+1); 
	}
	return dp[now];
}
int main(){
	fastio;
	R2(n,m);
	vector<mp> edges;
	rb(i,1,m){
		int u,v;
		R2(u,v);
		edges.PB(II(u,v));
		g[u].PB(v);
		g[v].PB(u);
	}
	rb(i,1,n)
	if(!vis[i]) dfs(i,0);
	res=n-cntBcc;
	rb(i,1,n)
		g[i].clear();
//	rb(i,1,n) cout<<belong[i]<<" ";cout<<endl; 
	rep(i,m){
		int u,v;
		u=edges[i].FIR;
		v=edges[i].SEC;
		if(belong[u]!=belong[v]){
			g[belong[u]].PB(belong[v]);
			g[belong[v]].PB(belong[u]);
		}
	}
	memset(vis,0,sizeof(vis));
	n=cntBcc;
	rb(i,1,n)
		val[i]=1;
	rb(i,1,n){
		if(!vis[i]){
			cntCc++;
			dfsCheck(i,0);
		}
	}
	rb(i,1,cntCc){
		bestValue[i]=INF;
		for(auto it:CC[i]){
			memset(calc,0,sizeof(calc));
			memset(dp,0,sizeof(dp));
			dfsInit(it,0);
			bestValue[i]=min(bestValue[i],sum[it]-dfsDp(it,0)-cntLeaves[it]+1);
		}
	}
	int addition=0;
	rb(i,1,cntCc){
		addition+=bestValue[i];
	}
	cout<<addition+res+cntCc-1<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值