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