【CQBZ模拟赛】星际旅行

描述

遥远的星系中共有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);
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值