这道题需要深刻的反思啊,一开始就思路不对,没有想到用欧拉路做,看了别人的代码才想到做法。
题意:有n个点,m条边,问如果选择m-2条边走2次,2条边只走1次,一共有多少种走法(当且仅当只走一次的边的集合不相同时,走法才不相同)
题解:首先只考虑所有路都走2遍的情况。在这种情况下,等效于把所有的边复制一次,那么只要整个图是一个联通块,这个图肯定是个欧拉回路,
因为所有的点的度都是偶数。
那么接下来就要考虑“删除”2条边,仍然能构成欧拉路的情况
①删去的2条边都是自环:删去后所有的点的度仍然是偶数,成立。
②删去的2条边中一条是自环:删去后只有2个点的度是奇数,成立。
③删去的2条边没有自环,且没有公共点:删去后有4个点的度是奇数,不成立。
④删去的2条边没有自环,且有公共点:删去后只有2个点的度是奇数,成立。
考虑完以上情况只有,只要枚举每条边,对于非自环的边,对答案的贡献是与他有公共点的边的数量+自环数;对于自环,对答案的贡献是m-1。最后将ans/2去掉重复情况即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MX = 1e6 + 5;
int u[MX],v[MX],mark[MX],IN[MX],p[MX],sz[MX];
int find(int x){return p[x]==x?x:(p[x]=find(p[x]));}
int main(){
//freopen("in.txt","r",stdin);
int n,m,cnt=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) p[i]=i,sz[i]=1;
for(int i=0;i<m;i++){
scanf("%d%d",&u[i],&v[i]);
if(u[i]==v[i]) {
cnt++;
mark[u[i]]=1;
continue;
}
int p1=find(u[i]),p2=find(v[i]);
if(p1!=p2){
p[p1]=p2;
sz[p2]+=sz[p1];
}
IN[u[i]]++;IN[v[i]]++;
}
bool flag=0;
int nn=n;
for(int i=1;i<=n;i++) if(sz[p[i]]==1&&mark[i]==0) nn--;
for(int i=1;i<=n;i++) if(sz[p[i]]==nn) flag=1;
if(flag){
LL ans=0;
for(int i=0;i<m;i++){
if(u[i]==v[i]){
ans+=(LL)(m-1);
}
else{
ans+=(IN[u[i]]-1)+(IN[v[i]]-1)+cnt;
}
}
printf("%I64d\n",ans/2);
}
else printf("0\n");
return 0;
}