Codeforces 788B Weird journey
description
给定一个n个点m条边的无向图
可以有自环
一条合法路径的要求是经过全部的m条边,其中2条边都经过一次,其余m-2条边经过两次
两条路径不同当且仅当其中有一条边在两种路径中经过次数不同
求路径数
input
第一行两个整数n,m
接下来m行每行两个整数u,v,表示u到v有一条边
output
一个整数表示答案
思路:
<摘自BerryKanry>
不同路径不同仅在某一条边经过次数不同,而路径要求每条边经过一次或者两次,等价于只要两条路径选择的只经过一次的两条边不同,换句话说就是任意两个不相同的边决定了两条不相同的路径。
考虑两条边作为路径上只经过一次的两条边,可以使得路径满足条件的条件,由于除了这两条边以外的边都要走两次,就是说走了还要走回来,先考虑所有的边走两次,相当于就是用一条线把整个图绕一圈,要想使得其中某条边只绕一次,就相当于绕一圈不绕完,留一点地方只走一次,而要求剩下两条边只走一次,很显然这两条边一定要挨在一起 。
。。。。。 反正就是个结论。
既然如此我们就记录每个点的度数,把每个点度数对于2的组合数,加起来,就行了,因为边挨在一起一定是有公共点,我们枚举整个公共点,看这个点相连的有多少条边,其中选两条的方案数。
看似没有问题,但这道题还需要考虑自环的情况,对于一条边,如果他是一个自环,那么就没有必须挨在一起的限制。
考虑两个自环作为一条路径的两条只走一次的边,考虑用线把整个图包起来,而自环反正走了和没走一样,只需要把这个图走两边,然后在自环的那两个点各跑一次自环就行了,不需要挨在一起
还需要处理自环加上一条不是自环的边的情况,自环数乘以自环数-1再除以2
也不需要挨在一起,一个是自环,另一个随便选一条边,不用除2
加上这两种情况之后就是答案
要注意图不一定连通,用并查集判一下,注意不是点的连通,而是所有的边要在一个连通块,等价于判断所有边的端点在一个连通块。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 100010;
int n, m, idc=0, idx=0;
int head[N], fa[N], cle[N];
LL ans, du[N];
int getfa(int x){
if(fa[x] == x) return x;
else return fa[x] = getfa(fa[x]);
}
int main(){
freopen ("tour.in", "r", stdin);
freopen ("tour.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) fa[i] = i;
for(int i=1; i<=m; i++){
int u, v; scanf("%d%d", &u, &v);
if(u != v) du[u]++, du[v]++;
else du[u]++, cle[++idx] = u;
int a = getfa(u), b = getfa(v);
if(a != b) fa[a] = b;
}
int ff;
for(int i=1; i<=n; i++){
if( du[i] ){
ff = getfa(i);
break;
}
}
for(int i=2; i<=n; i++){
int cc = getfa(i);
if(ff != cc && du[i]) {
printf("0\n");//判是否有边不连通
return 0;
}
}
for(int i=1; i<=n; i++){
ans += du[i] * (du[i] - 1) / 2;
}
for(int i=1; i<=idx; i++){
ans += m - du[cle[i]] - idx + 1;//自环与非自环
}
ans += idx * (idx - 1) / 2;//自环与自环
cout << ans << endl;
return 0;
}