题解 星际旅行

传送门

考场上我再一次坚持认为这是一道组合数题
考场上真的会见到组合数题嘛我认为错好几次了
所以除非真的能确定是组合数否则不要当组合数写了

可以先用和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;
}
posted @ 2021-06-18 16:09  Administrator-09  阅读(27)  评论(0编辑  收藏  举报