题目
问题描述
总共有 n n n 个节点, m m m 条路径,要求其中 m − 2 m-2 m−2 条路径走两遍,剩下 2 2 2 条路径仅走一遍,问不同的路径总数有多少,如果仅走一遍的两条边不同则将这两条路径视为不同。
输入格式
- 第一行两个整数n和m,表示图有n个节点m条边,数据保证不存在重边
- 接下来m行,每行两个整数u,v,表示节点u和节点v之间有一条无向边
输出格式
- 一个整数,表示方案数
数据范围
1 ≤ n , m ≤ 1 × 1 0 6 1\le n,m\le 1\times10^6 1≤n,m≤1×106
输入/输出例子
输入
5 4
1 2
1 3
1 4
1 5
输出
6
提示
解题思路
分析
考虑直接将每条边都当作两条边,那么题意及从 2 n 2n 2n 条边任选 2 2 2 条不是连接相同节点的边删除后图存在欧拉路,求删边的方案数。
如果本来的图不连通,那么肯定没有合适的方案,直接输出 0 0 0 就可以了。
否则我们在分析:由于我们是将每条边当作两条边的,那么对于建的图来说,每个顶点的度数为偶数,及一定存在欧拉路。
那么就来考虑删的边了。
我们可以把边分为自环和非自环两类。
为什么呢?
因为自环只跟一个顶点有关系,而非自环跟两个顶点有关系。
如果删除了一个自环,那剩下的所有点的度数仍然为偶数,所以可以随意删除下一条边。
而对于删除的第二条边,如果是非自环,那么图就有两个积点,存在欧拉路径,如果是自环,那么图所有点为偶点,存在欧拉回路。
所以可以得出自环的贡献为:
c
n
t
×
(
m
−
c
n
t
)
+
c
n
t
×
(
c
n
t
−
1
)
/
2
cnt \times (m-cnt)+cnt\times (cnt-1)/2
cnt×(m−cnt)+cnt×(cnt−1)/2
其中
c
n
t
cnt
cnt 为自环数,
m
m
m 是边数。
如果删除两条非自环,如果可行,那么它们一定共顶点,只有这样才会使共顶点的度数 − 2 -2 −2,仍为偶点,两外两个点的度数 − 1 -1 −1,为积点,存在欧拉路径。
所以非自环的贡献为:
∑
i
=
1
n
l
i
×
(
l
i
−
1
)
2
\sum_{i=1}^{n}\frac{l_i\times(l_i-1)}{2}
∑i=1n2li×(li−1)
其中
l
i
l_i
li 为每个顶点连接的边数(不包括自环)。
Code
#include<bits/stdc++.h>
#define int long long
const int N=1e6+1;
using namespace std;
vector<int>E[N];
int n,m,x[N],y[N],deg[N],ans,vis[N],cnt,fa[N];
inline int ga(int x)
{
return x==fa[x]?fa[x]:fa[x]=ga(fa[x]);
}
inline void uni(int x,int y)
{
int fx=ga(x),fy=ga(y);
if(fx!=fy)
fa[fx]=fy;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL),cout.tie(NULL);
cin>>n>>m;
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
cin>>x[i]>>y[i];
uni(x[i],y[i]);
if(x[i]==y[i])
cnt++;
else
E[x[i]].push_back(y[i]),E[y[i]].push_back(x[i]);
}
for(int i=2;i<=m;i++)
if(ga(x[i])!=ga(x[1]))
cout<<"0",exit(0);
for(int i=1;i<=n;i++)
{
int le=E[i].size();
ans+=le*(le-1)/2;
}
ans+=cnt*(cnt-1)/2;
ans+=cnt*(m-cnt);
cout<<ans;
return 0;
}