1. 程式人生 > >noip模擬賽(關於歐拉回路的一點想法)

noip模擬賽(關於歐拉回路的一點想法)

題目大意:給出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);
}