世界真的很大
这道题和欧拉回路其实关系不大,不要被标题吓住了,这道题主要还是一道奇妙题,算法什么的都只是辅助工具而已
考试的时候想出来了60%吧,还是忽略了些情况
任何时候对题目的逆向分析都很重要,尤其是对于那些压根儿看不出算法的题
看题先:
description
给定一个n个点m条边的无向图
可以有自环
一条合法路径的要求是经过全部的m条边,其中2条边都经过一次,其余m-2条边经过两次
两条路径不同当且仅当其中有一条边在两种路径中经过次数不同
求路径数
input
第一行两个整数n,m
接下来m行每行两个整数u,v,表示u到v有一条边
output
一个整数表示答案
先从题目入手
首先分析路径是一个什么东西
不同路径不同仅在某一条边经过次数不同,而路径要求每条边经过一次或者两次,等价于只要两条路径选择的只经过一次的两条边不同
换句话说就是任意两个不相同的边决定了两条不相同的路径
有多少条互不相同的路径等价于有多少对不同的边有这样的一条路径
假设任意两条边都可以形成满足条件的路径的话,那答案就是边数对2的组合数了
考虑两条边作为路径上只经过一次的两条边,可以使得路径满足条件的条件,,的条件
由于除了这两条边以外的边都要走两次,就是说走了还要走回来
先考虑所有的边走两次,相当于就是用一条线把整个图绕一圈
要想使得其中某条边只绕一次,就相当于绕一圈不绕完,留一点地方只走一次,而要求剩下两条边只走一次,很显然这两条边一定要挨在一起
。。。。。
好吧我知道我讲的并不清楚,语文不好,见谅,但是一定要好好想想,理解一下这两条边一定是挨在一起的,这样
。。。。。
既然如此我们就记录每个点的度数,把每个点度数对于2的组合数,加起来,就行了,因为边挨在一起一定是有公共点,我们枚举整个公共点,看这个点相连的有多少条边,其中选两条的方案数
看似没有问题但并不止步于此,但是我考试的时候就只想了这么多233,就只有40分
这道题还需要考虑自环的情况,对于一条边,如果他是一个自环,那么就没有必须挨在一起的限制
考虑两个自环作为一条路径的两条只走一次的边,考虑用线把整个图包起来,而自环反正走了和没走一样,只需要把这个图走两边,然后在自环的那两个点各跑一次自环就行了,不需要挨在一起
还需要处理自环加上一条不是自环的边的情况,自环数乘以自环数-1再除以2
也不需要挨在一起,一个是自环,另一个随便选一条边,不用除2
加上这两种情况之后就是答案
有需要注意的地方:
1,图不一定连通,用并查集判一下,注意不是点的连通,而是所有的边要在一个连通块,等价于判断所有边的端点在一个连通块里面,并查集判断一下就好
完整代码:
#include<stdio.h>
#include<iostream>
using namespace std;
typedef long long dnt;
int n,m,fa[1000010],a[1000010],du[1000010],sig=0;
int getfather(int x)
{
if(fa[x]==x) return x;
return fa[x]=getfather(fa[x]);
}
int main()
{
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);
a[i]=u;
int x=getfather(u),y=getfather(v);
if(x!=y) fa[y]=x;
if(u!=v)
du[u]++,du[v]++;
else
sig++;
}
int tmp=getfather(a[1]);
for(int i=2;i<=m;i++)
if(getfather(a[i])!=tmp)
{
printf("0\n");
return 0;
}
dnt ans=0;
for(int i=1;i<=n;i++)
ans+=1ll*du[i]*(du[i]-1)/2;
ans+=1ll*sig*(sig-1)/2;
ans+=1ll*sig*(m-sig);
cout << ans << endl;
return 0;
}
/*
EL PSY CONGROO
*/
嗯,就是这样