CF788B Weird journey

2019.8.25

Description

遥远的星系中共有n颗行星,由m个双向虫洞所连接。两颗不同行星间最多有1个虫洞直接相连,但一个虫洞的两端可能连接同一颗行星。

一条星际旅行的航线需要满足以下要求:从任意一颗行星出发,在任意一颗行星上结束,总共经过m − 2个虫洞恰好2次,经过2个虫洞恰好1次。

现在我们想要知道,有多少种本质不同的旅行航线。两条航线被认为本质不同,当且仅当至少存在一个虫洞,在两条航线中经过的次数不同。

Solution

要求 \(m-2\) 个虫洞都要经过两次,所以先不妨把所有边都建两次,然后又要求有两个虫洞只能经过一次,就枚举两条边把它们删去,都只剩下一条,最后要求的就是欧拉路径。

删边的分类讨论:

众所周知,欧拉路径是否存在与图中每个点的度有关,首先因为每条边建了两次,所以初始时所有点的度都是偶数,删边会影响到度数,而普通边与自环的影响效果又不同,所以要分类讨论。

1.删两条普通边。非常显然,这两条边必须是相邻的,否则的话会有四个点的度数都减了一,也就是会有四个奇点,不符合要求,而如果是相邻的话,公共节点度数减 \(2\),没有影响,剩下两个奇点。
具体统计方案的话,只需要枚举所有节点,然后对于一个节点,连接它的所有边一定是相邻的,假设有 \(m\) 条边,则贡献为 \(\binom{m}{2}\),比较显然。

2.一条普通边,一个自环。自环会使它所连的节点度数减 \(2\),没有影响,而另一条普通边会使得出现两个奇点,符合要求。
贡献为:总的自环个数 \(\times\) 普通边数

3.两个自环。前面说到自环没有影响,所以贡献为 \(\binom{自环个数}{2}\)

#include<stdio.h>
#define N 100007
#define ll long long

template<class T>
inline void read(T &x){
	x=0;char c=getchar();T flag=1;
	while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
	x*=flag;
}

int n,m,fa[N],in[N],cnt;
inline int find(int x){
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}
int main(){
	freopen("tour.in","r",stdin);
	freopen("tour.out","w",stdout);
	read(n),read(m);
	int u,v;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++){
		read(u),read(v);
		if(u==v){cnt++;continue;}
		in[u]++,in[v]++;
		u=find(u),v=find(v);
		fa[v]=u;
	}
	int ret=0;
	for(int i=1;i<=n;i++)
		if(in[i]){
			if(!ret) ret=find(i);
			else if(find(i)!=ret){
				printf("0");
				return 0;
			}
		}
	if(!ret){printf("0");return 0;}
	ll ans=0;
	for(int i=1;i<=n;i++)
		ans+=((ll)in[i]*(in[i]-1))>>1;
	ans+=(ll)cnt*(m-cnt);
	ans+=((ll)cnt*(cnt-1))>>1;
	printf("%lld",ans);
}
posted @ 2019-08-25 18:55  Kreap  阅读(103)  评论(0编辑  收藏  举报