[CSP-S模拟测试]:星际旅行(欧拉路)
题目传送门(内部题4)
输入格式
第一行两个整数$n,m$,表示行星和虫洞的数量。
接下来$m$行,每行两个整数$u,v$,表示存在一个双向虫洞直接连接$u$和$v$。
每一个虫洞最多会被描述一次。
输出格式
一行一个整数,代表本质不用的航线的数量。
样例
样例输入
5 4
1 2
1 3
1 4
1 5
样例输出
6
数据范围与提示
样例解释:
本质不同的航线有$6$条:
2-1-3-1-4-1-5
2-1-3-4-5-1-4
2-1-4-1-5-1-3
3-1-2-1-5-1-4
3-1-2-1-4-1-5
4-1-2-1-3-1-5
注意2-1-4-1-3-1-5不是另一个本质不同的航线,它与第一条航线是本质相同的。
限制与约定:
对于$10\%$的数据,$n,m \leqslant 5$。
对于$20\%$的数据,$n,m \leqslant 10$。
对于$40\%$的数据,$n,m \leqslant 100$。
对于$60\%$的数据,$n,m \leqslant 1000$。
对于所有的数据,$1 \leqslant n,m \leqslant {10}^5,1 \leqslant u,v \leqslant n$。
题解
将所有的边拆成两条,问题变成删掉两条边,使得剩下的图是一个欧拉路或者是欧拉回路。
则分为三种情况:
1.删掉两个自环:删掉两个自环,这两个点的度数都$-2$,成为一个欧拉回路,假设这样的点有$sum$个,则方案数为$C_{sum}^2$。
2.删掉一个自环和一条边:删自环上面已经说明了,考虑删边,删去一条边则这两个点的度数$-1$,变为两个奇数点,成为一个欧拉路,方案数为$C_{sum}^2 \times (m-sum)$。
3.删掉有一个公共点的两条边:该点的度数$-2$,两条边分别连接的另两个点的度数分别$-1$,成为一个欧拉路,方案数为$\sum \limits_{i=1}^n C_{d[i]}^2$。
则总的方案数为:$C_{sum}^2 + C_{sum}^2 \times (m-sum) + \sum \limits_{i=1}^n C_{d[i]}^2$。
至于计算过程,显然上面的方法不方便计算,那么我们考虑转化一下计算的思路。
将情况$1$和情况$2$和起来,则第一个自环可以与剩余边结合,方案数为$m-1$,第二个自环贡献的方案数为$m-2$,根据高中课本中等差数列求和公式可得方案数为:$\frac{(2 \times m-sum-1) \times sum}{2}$。
情况3的方案数为:$\frac{\sum \limits_{i=1}^n d[i] \times (d[i]-1)}{2}$,扫一边即可求出。
注意图不连通的判断方式,不是点不连通,而是边不连通,可以使用并差集判断,不联通方案数为$0$。
代码时刻
#include<bits/stdc++.h>
using namespace std;
int n,m;
int rd_geq[100001],rd_leq[100001],f[100001];
long long ans,sum;
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void connect(int x,int y){f[find(x)]=find(y);}
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 x,y;
scanf("%d%d",&x,&y);
rd_leq[x]++;//所有的度
rd_leq[y]++;
if(x==y)sum++;//统计自环
else
{
rd_geq[x]++;//非自环的度
rd_geq[y]++;
connect(x,y);
}
}
int flag=0;
for(int i=1;i<=n;i++)
if(rd_leq[i])
{
flag=i;
break;
}
for(int i=1;i<=n;i++)
{
if(rd_leq[i]&&find(i)!=f[flag])
{
puts("0");
return 0;
}
ans+=1LL*(rd_geq[i]-1)*rd_geq[i]/2;//第3种情况
}
ans+=sum*(m-sum)+sum*(sum-1)/2;//第1种和第2种情况
printf("%lld",ans);
return 0;
}
rp++