1. 程式人生 > 實用技巧 >[CF788B Weird journey] 星際旅行(尤拉路)

[CF788B Weird journey] 星際旅行(尤拉路)

CF788B [Weird Jouney]

原題目地址

一句話題意:總共有\(n\)個節點,\(m\)條路徑,要求其中\(m-2\)條路徑走兩遍,剩下\(2\)條路徑僅走一遍,問不同的路徑總數有多少,如果僅走一遍的兩條邊不同則將這兩條路徑視為不同。

Solution

直接把邊拆成兩條,顯然這樣每個點的度數都是偶數
整個圖構成尤拉路
題目意思就是說刪去兩條邊之後仍然構成尤拉路
那麼根據尤拉路的性質可以知道
刪去的兩條邊必連在同一個點上
這樣保證了有兩個點的度數變成奇數的情況下
和他們連的那個點度數-2,仍然為偶數
整張圖依然是尤拉路

下面考慮對答案有貢獻的三種情況\((circle為自環數量)\)

  • 兩個自環對答案做貢獻,總貢獻\(\frac {circle * (circle - 1) } 2\)建議手模
  • 一個自環一條邊,貢獻值\((circle) * (m - circle)\)
  • 兩條邊\(\frac {(du[i] * (du[i] - 1)} 2\)

注意

  • 統計答案開\(long \:\ long\),最後一種兩條邊的情況也要開
  • 判斷整張圖是否連通,若不連通輸出0,可以選擇冰茶几或者dfs,總之隨便搞搞都能判

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

inline int read(){
	int x = 0, w = 1;
	char ch = getchar();
	for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

const int ss = 2000010;

int fa[ss], du[ss];

inline int find(int u){
	if(u == fa[u]) return u;
	return fa[u] = find(fa[u]);
}

inline void link(int u, int v){
	int xx = find(u), yy = find(v);
	if(xx != yy) fa[xx] = yy;
}

long long ans;
int l[ss], r[ss];
int circle;

signed main(){
	int n = read(), m = read();
	for(int i = 1; i <= n; i++) fa[i] = i;
	for(int i = 1; i <= m; i++){
		l[i] = read(), r[i] = read();
		link(l[i], r[i]);
		if(l[i] == r[i]) circle++;
		else{
			du[l[i]]++;
			du[r[i]]++;
		}
	}
	int begin = find(l[1]);
	for(int i = 2; i <= m; i++)
		if(find(l[i]) != begin)
			return puts("0"), 0;
	ans += (circle - 1) * circle / 2;
	ans += (circle) * (m - circle);
	for(int i = 1; i <= n; i++)
		ans += (long long)du[i] * (du[i] - 1) / 2;
	printf("%lld\n", ans);
	return 0;
}