描述
遥远的星系中共有n颗行星,由m个双向虫洞所连接。两颗不同行星间最多
有1个虫洞直接相连,但一个虫洞的两端可能连接同一颗行星。一条星际旅行的
航线需要满足以下要求:从任意一颗行星出发,在任意一颗行星上结束,总共
经过m − 2个虫洞恰好2次,经过2个虫洞恰好1次。现在我们想要知道,有多少
种本质不同的旅行航线。两条航线被认为本质不同,当且仅当至少存在一个虫
洞,在两条航线中经过的次数不同。
输入
第一行两个整数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 - 1 - 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 不是另一个本质不同的航线,它与第一条航线是本
质相同的。
2
限制与约定
对于10%的数据,n, m ≤ 5。
对于20%的数据,n, m ≤ 10。
对于40%的数据,n, m ≤ 100。
对于60%的数据,n, m ≤ 1000。
对于所有数据,1 ≤ n, m ≤ 10^5,1 ≤ u, v ≤ n。
思路
由于m-2条边为2次,2条边1次,则其一定为一个连通图。
则若其不为连通图,即可以直接输0。(必须为 边的连通图)
当我们已经确定其为一个边的连通图后。
假设没有自环,考虑怎么做。假设我们从某点出发(大联通块内的点),经过m-2条边2次,2条边1次。
将此图每一条边都变成两条边(满足题意,此时每个点的出度都为偶数),则根据欧拉回路性质,我们从此点出发,必定会回到此点,则往回走两步(非同一条边),即为一个合法路径。
加入自环,将自环分离出来,若有一个自环,则其贡献方案数为非自环边数与总自环数-1(减去本身),即经过此点时走一次自环,再枚举剩下的任意一条边(包括自环),即从那条边任意端点往外出发,经过此自环时走一次的路径。
方案数即为以上情况(0自环,1自环,2自环)(但要注意判重)。
Code
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=100005;
int n,m,vis[MAXN],fig[MAXN];
vector<int>P[MAXN];
long long ans,fx1,fx2;
void dfs(int u,int fa){
vis[u]=1;
int size=P[u].size();
for(int i=0;i<size;i++){
int v=P[u][i];
if(v==fa)continue;
if(vis[v])continue;
dfs(v,u);
}
}
int main(){
//freopen("tour.in","r",stdin);
//freopen("tour.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
if(x!=y){
fx1++;
P[x].push_back(y);
P[y].push_back(x);
}
else fx2++,fig[x]=1;
}
for(int i=1;i<=n;i++)
if(P[i].size()){
dfs(i,0);break;
}
for(int i=1;i<=n;i++)
if(P[i].size()&&!vis[i]){
printf("0\n");
return 0;
}
for(int i=1;i<=n;i++)
if(!P[i].size()&&fig[i]){
printf("0\n");
return 0;
}
for(int i=1;i<=n;i++){
int size=P[i].size();
for(int j=0;j<size;j++){
int v=P[i][j];
ans+=P[v].size()-1;
}
}
ans+=fx2*fx1*2+fx2*(fx2-1);
printf("%lld\n",ans/2);
}