Bzoj4784: [Zjoi2017]仙人掌

题面

传送门

Sol

首先判断是能成为仙人掌

然后考虑(DP)
因为所有的环内不可能连边,那么直接删掉
变成一个森林
对每个树求出方案然后相乘就是答案

一个巧妙的转化:看成选取若干条路径恰好覆盖所有的树边的方案数

(g[i])表示(i)个点两两配对的方案数
(g[i]=g[i-1]+g[i-2]*(i-1))
即要么不配对,要么选一个配对

(f[i])表示子树的方案数
首先(f[u]=Pi_{vin{son(u)}} f[v])
(sum)(u)的儿子数
如果(u)不为根,那么还可以有一个点向上匹配(f[u]+=g[sum+1])
否则(f[u]+=g[sum])

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;

IL int Input(){
	RG int x = 0, z = 1; RG char c = getchar();
	for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
	for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
	return x * z;
}

const int maxn(5e5 + 5);
const int mod(998244353);

int n, m, dfn[maxn], low[maxn], idx, g[maxn], f[maxn], cactus, sta[maxn], top, col[maxn];
bool vis[maxn << 2];

struct Edge{
	int first[maxn], cnt, nxt[maxn << 2], to[maxn << 2];

	IL void Init(){
		cnt = 0, Fill(first, -1);
	}

	IL void Add(RG int u, RG int v){
		vis[cnt] = 0, nxt[cnt] = first[u], to[cnt] = v, first[u] = cnt++;
	}
} e1;

IL void Tarjan(RG int u, RG int fe){
	dfn[u] = low[u] = ++idx, sta[++top] = u;
	RG int flg = 0;
	for(RG int e = e1.first[u]; e != -1; e = e1.nxt[e]){
		if(e == fe) continue;
		RG int v = e1.to[e];
		if(!dfn[v]){
			Tarjan(v, e ^ 1), low[u] = min(low[u], low[v]);
			if(low[v] < dfn[u]){
				if(flg) cactus = 0;
				flg = 1;
			}
		}
		else{
			low[u] = min(low[u], dfn[v]);
			if(dfn[v] < dfn[u]){
				if(flg) cactus = 0;
				flg = 1;
			}
		}
	}
	while(low[u] == dfn[u]){
		col[sta[top--]] = u;
		if(sta[top + 1] == u) break;
	}
}

IL void Upd(RG int &x, RG int y){
	x += y;
	if(x >= mod) x -= mod;
}

IL void Solve(RG int u, RG int rt){
	f[u] = dfn[u] = 1; RG int sum = 0;
	for(RG int e = e1.first[u]; e != -1; e = e1.nxt[e]){
		RG int v = e1.to[e];
		if(dfn[v] || vis[e]) continue;
		Solve(v, rt);
		f[u] = 1LL * f[u] * f[v] % mod;
		++sum;
	}
	if(u != rt) f[u] = 1LL * f[u] * g[sum + 1] % mod;
	else f[u] = 1LL * f[u] * g[sum] % mod;
}

int main(){
	for(RG int t = Input(); t; --t){
		e1.Init(), cactus = 1, top = idx = 0, n = Input(), m = Input();
		for(RG int i = 1; i <= n; ++i) col[i] = dfn[i] = low[i] = f[i] = 0;
		for(RG int i = 1, a, b; i <= m; ++i)
			a = Input(), b = Input(), e1.Add(a, b), e1.Add(b, a);
		g[0] = g[1] = 1;
		for(RG int i = 2; i <= n + 1; ++i)
			g[i] = g[i - 1], Upd(g[i], 1LL * (i - 1) * g[i - 2] % mod);
		Tarjan(1, -1);
		if(!cactus){
			puts("0");
			continue;
		}
		for(RG int i = 1; i <= n; ++i)
			for(RG int e = e1.first[i]; e != -1; e = e1.nxt[e])
				if(col[i] == col[e1.to[e]]) vis[e] = 1;
		for(RG int i = 1; i <= n; ++i) dfn[i] = 0;
		RG int ans = 1;
		for(RG int i = 1; i <= n; ++i)
			if(!f[i]) Solve(i, i), ans = 1LL * ans * f[i] % mod;
		printf("%d
", ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/cjoieryl/p/9116046.html