1. 程式人生 > 其它 >題解 星際旅行

題解 星際旅行

傳送門

考場上我再一次堅持認為這是一道組合數題
考場上真的會見到組合數題嘛我認為錯好幾次了
所以除非真的能確定是組合數否則不要當組合數寫了

可以先用和DZY相同的思路處理取模
考場上有想到尤拉路,但是想偏了
考場思路:
因為一個連邊數均為偶數的無向圖(去掉自環)一定可以一筆畫出
則對於連邊數均為偶數的情況,等價於求(\(cnt\)為邊數)\(C_{cnt}^2\)
而對於有連出奇數邊的情況,因為在到達有奇數個連邊的點時可以通過走一遍並返回”消掉“一條邊,則轉化為在有奇數個連邊的點中選兩個作為始末點的方案數
不過這樣顯然不對但是資料水可以考慮騙10pts
因為有偶數連邊的點(有時候)同樣可以作為始末點,這裡無法對這種情況進行判斷

然後是正解:
還是考慮尤拉路,
這裡把每條無向邊顯式地拆成兩條有向邊,這樣就是真正的尤拉路了
考慮刪去兩條邊後使圖仍為尤拉路的方案數
其實考場上有想到刪邊,但是一想刪邊就開始想列舉刪哪兩條邊再跑check,直接O(n^2)還常數巨大就沒再想
注意這個圖「本來是尤拉路」,刪邊後要使圖「仍為尤拉路」
顯然拆邊後每個點出入度為偶數
考慮刪邊後「仍為偶數」:

  • 刪兩個自環:顯然
  • 直接刪一條無向邊(要保證刪完後所有邊連通)

考慮刪邊後「有兩個奇數」:
刪自環不產生奇數點,刪一條正常的邊會產生兩個奇數點,所以

  • 刪一個自環,再隨便刪條正常邊
  • 刪兩條有共同頂點的邊(所共的頂點-2仍為偶數)(這裡包括了上面第二個)

組合數求就好了

這裡有個小坑,就是邊可能不連通,此時沒有方案對第一組資料就沒有連通的邊
這裡題意轉化的就很難想……是在考慮轉化為「×××後仍滿足×××條件的方案數」?
這樣就是找條件跑組合數了(?)

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
int head[N], size=1, cnt[N], fa[N];
ll tot, self;
struct edge{int to, next;}; edge* e;
inline void add(int s, int t) {edge *k=&e[++size]; k->to=t; k->next=head[s]; head[s]=size;}
inline int find(int p) {return fa[p]==p?p:fa[p]=find(fa[p]);}

inline ll C(ll n) {return n*(n-1)/2;}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	ll ans=0;
	
	n=read(); m=read();
	e = new edge[m*2+10];
	for (int i=1; i<=n; ++i) fa[i]=i;
	for (int i=1,u,v; i<=m; ++i) {u=read(); v=read(); add(u, v); add(v, u); if (u!=v) {++cnt[u]; ++cnt[v]; ++tot; fa[find(v)]=find(u);} else ++self;}
	int p=find(e[2].to);
	for (int i=4; i<=size; i+=2) if (find(e[i].to)!=p) {puts("0"); return 0;}
	ans += C(self)+self*tot;
	//for (int i=1; i<=n; ++i) cout<<cnt[i]<<' '; cout<<endl;
	for (int i=1; i<=n; ++i) ans+=C(cnt[i]);
	printf("%lld\n", ans);

	return 0;
}