CF788B Weird journey
2019.8.25
Link
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);
}