矿场搭建

今天我这一台机器好像不知道怎么回事,好像被限速了似的。贼慢

今天晚上还有可能回去上万恶的文化课

我好郁闷呀。

不过今天也终于将点双和边双彻底理解了

%%% tarjan

题目入口

一开始没有考虑割点能存在于不同的点双中。

考虑到了以后又在纠结如何计数233

结果发现竟然是最简单的乘法原理233

割点数量 (0) (1) (>=2)
方案数 (C^2_n) (n-1) $$0$$

什么意思呢,当一个点双中没有割点时,那么放哪都可以,不过要放两个。如果只放一个的话,如果碰巧就是这个塌了。那就GG了

当点双中只有一个割点时,那么放在这个点双的任意一个位置就行了。如果割点被GG了。我们有在点双中的逃生出口。如果点双中的逃生出口GG了。我们就可以通过割点溜之大吉。

如果有两个及以上的割点。因为只会塌方一个,所以无论怎么塌,都可以转移到其他的点双中。就不用再建立了。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int m,n;
int dfn[510],low[510];
int g_num;
vector<int>g[510];
vector<int>l[510];
int t,root;
bool p[510];
int stack[510],top;
void tarjan(int now)
{
	dfn[now]=low[now]=++t;
	stack[++top]=now;
	int chi=0;
	for(int i=0;i<l[now].size();i++)
	{
		int nxt=l[now][i];
		if(!dfn[nxt])
		{
			chi+=1;
			tarjan(nxt);
			low[now]=min(low[now],low[nxt]);
			if((now==root&&chi>1)||(now!=root&&dfn[now]<=low[nxt]))	
				p[now]=true;
			if(low[nxt]>=dfn[now])
			{
				int pas;
				g_num+=1;
				do
				{
					pas=stack[top--];
					g[g_num].push_back(pas);
				}while(pas!=nxt);
				g[g_num].push_back(now);
			}
		}
		else	low[now]=min(dfn[nxt],low[now]);
	}
}
void solve()
{
	for(int i=1;i<=n;i++)
		g[i].clear(),p[i]=false,l[i].clear(),dfn[i]=0,low[i]=0;
	n=0;
	t=0;g_num=0;
	int a,b;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		l[b].push_back(a);
		l[a].push_back(b);
		a=max(a,b);
		n=max(a,n);
	}
	long long set=0,way=1;
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			root=i,tarjan(i);
	for(int i=1;i<=g_num;i++)
	{
		int cut=0;
		for(int j=0;j<g[i].size();j++)
			if(p[g[i][j]])	cut+=1;
		int tot=g[i].size();
		if(!cut)	set+=2ll,way*=1ll*(tot-1)*tot/2;
		if(cut==1)	set+=1ll,way*=1ll*(tot-1);
	}
	//printf("%lld %lld
",set,way);
	cout<<set<<" "<<way<<endl;
}
int main()
{
	freopen("input.in","r",stdin);
	freopen("input.out","w",stdout);
	scanf("%d",&m);
	int tot=0;
	while(m)
	{
		printf("Case %d: ",++tot);
		solve();
		scanf("%d",&m);
	}
}
原文地址:https://www.cnblogs.com/Lance1ot/p/9230592.html