「解题报告」SP16185 Mining your own business

「解题报告」SP16185 Mining your own business

原题传送门

题意

给你一个无向图,求至少安装多少个太平井,才能使不管那个点封闭,其他点都可以与有太平井的点联通。

题解

其他题解里都说要用“点双连通分量”,其实不用。

我们先用tarjan求出所有割点,然后我们假设所有的割点都坍塌掉了,整张图就被我们分成了许多联通块,我们可以用暴搜找出所有联通块。每个联通块与割点有如下关系(s表示一个联通块的点的数量):

  • 这个联通块与0个割点直接连接:说明这个联通块与其他联通块不相连。此时要在这个联通块里安两个太平井才可以(有可能一个太平井坍塌了,所以要两个)。方案数为 \(\frac{n*(n-1)}{2}\)

  • 这个联通块与1个割点直接连接:如果与它相连的这个割点坍塌掉了,那这个联通块的所有点就逃不出去了,所以我们要往这个联通块里放一个太平井,即使这个太平井坍塌了,这个联通块的其它点也可以从割点逃出去。方案数为 \(s\)

  • 这个联通块与2及以上个割点直接连接:不管周围那个割点坍塌掉了,这个联通块的点都可以从其它割点逃出去,所以不用安太平井。

Code

#include <bits/stdc++.h>
#define _for(i,a,b) for(ll i=a;i<=b;++i)
#define for_(i,a,b) for(ll i=a;i>=b;--i)
#define ll long long
using namespace std;
const ll N=50010,inf=0x3f3f3f3f;
ll T,n,m,x,y,ans1,ans2,cnt_ge[N];
ll dfn[N],low[N],jl[N],cnt,num,c[N],ge[N];
ll df[N],s,co[N];
vector<ll>tu[N];
stack<ll>st;
map<ll,map<ll,ll> >jl_ge;
void clear(){
	n=0,m=0,x=0,y=0,ans1=0,ans2=1,cnt=0,num=0,s=0;
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(jl,0,sizeof(jl));
	memset(c,0,sizeof(c));
	memset(ge,0,sizeof(ge));
	memset(df,0,sizeof(df));
	memset(cnt_ge,0,sizeof(cnt_ge));
	memset(co,0,sizeof(co));
	jl_ge.clear();
	_for(i,1,N)tu[i].clear();
}void tarjan(ll u,ll root){
	low[u]=dfn[u]=++cnt;
	st.push(u),jl[u]=1;
	ll sz=tu[u].size(),x=0,sum=1;
	_for(i,0,sz-1){
		ll v=tu[u][i];
		if(!dfn[v]){
			tarjan(v,root),low[u]=min(low[u],low[v]);
			if(dfn[u]<=low[v])
				if(u!=root||(++x)>1)ge[u]=1;
		}else low[u]=min(low[u],dfn[v]);
	}
}void dfs(int u,int fa,int color){
	int sz=tu[u].size();
	++co[color],df[u]=1;
	_for(i,0,sz-1){
		int v=tu[u][i];
		if(v==fa||df[v])continue;
		if(ge[v]){
			if(!jl_ge[color][v]){
				++cnt_ge[color];
				jl_ge[color][v]=1;
			}continue;
		}dfs(v,u,color);
	}
}int main(){
	while(++T){
		clear();
		scanf("%lld",&m);
		if(m==0)break;
		_for(i,1,m){
			scanf("%lld%lld",&x,&y);
			tu[x].push_back(y);
			tu[y].push_back(x);
			n=max(n,max(x,y));
		}_for(i,1,n)if(!dfn[i])tarjan(i,i);
		_for(i,1,n){
			if(!ge[i]&&!df[i]){
				dfs(i,i,++s);
				if(cnt_ge[s]==1)++ans1,ans2*=co[s];
				if(cnt_ge[s]==0)ans1+=2,ans2*=co[s]*(co[s]-1)/2;
			}
		}printf("Case %lld: %lld %lld\n",T,ans1,ans2);
	}return 0;
}
posted @ 2022-02-13 16:00  K8He  阅读(72)  评论(0编辑  收藏  举报