noip模拟赛(关于欧拉回路的一点想法)

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 https://blog.csdn.net/zzk_233/article/details/83069102

题目大意:给出n个点m条边,问有多少种方案可以走m-2条边2次,走2条边1次。边为双向边。无重边,有自环。

这道题用到了欧拉回路的一些思想(考试的时候我是通过对拍出所有情况讨论过的!)

可以理解成花一个一笔画,共用了2*m-2条边。这种用不重复的边走完全图的操作就是欧拉回路。

而根据欧拉回路的性质,只有所有的点的度都是偶数或者只有两个点的度是奇数就可以满足。

而因为是双向边,所以所有点的度都是偶数, 那么就只有以下几种情况:

1.任意三个相连的点,连接他们的两条边去掉。这样中间点的度还是偶数,左右点的度为奇数,满足。

2.删去一个自环和一条任意的边,删去自环仍然是偶数,一条边删去出现两个奇数点。

3.删去任意两个自环,所有点还都是偶数。

所以只要在每个点的度中任取两个都可以构成答案,对于环与边的答案要单独处理,对于环之间的答案一起处理。

任取两个的方案数就是C(2,num[i](i点的度)),也就是num[i]*(num[i]-1)/2。

最后对于图不连通的情况输出0,不连通是指有边的点之间不属于一个集合。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
ll ans;
ll num[100005];
int f[100005];
int used[100005];
int tt;
int cb[100005],ans1;
int findf(int x)
{
	if(x==f[x])return x;
	return f[x]=findf(f[x]);
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=m;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		num[a]++;num[b]++;
		if(a==b)
		{
			num[a]--;
			cb[++ans1]=a;
		}
		int f1=findf(a),f2=findf(b);
		if(f1!=f2)f[f1]=f2;
		used[a]++;used[b]++;
	}
	for(int i=1;i<=n;i++)
	{
		if(!used[i])continue;
		if(findf(i)==i)tt++;
		if(tt>1)
		{
			printf("%d",0);
			return 0;
		}
	}
	for(int i=1;i<=n;i++)
	{
		ans+=(ll)num[i]*(ll)(num[i]-1)/(ll)2;
	}
	for(int i=1;i<=ans1;i++)
	{
		ans+=(ll)m-(ll)num[cb[i]]-(ll)ans1+(ll)1;
	}
	ans+=(ll)ans1*(ll)(ans1-1)/(ll)2;
	printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/zzk_233/article/details/83069102