Codeforces 788B Weird journey (欧拉回路 组合数计数)

来源:互联网 发布:多台docker php-fpm 编辑:程序博客网 时间:2024/05/27 19:26

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 longusing 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;}