[ZJOI2017]仙人掌

题面

luogu

分析

如果本来就不是仙人掌那就凉啦
特判仙人掌的话
先dfs建树建fa
然后对于每一条边 从它dfn大的那个点开始跳fa
把跳过的边打标记
显然有环的话 环上的边会被跳两次
如果一个边被多个环覆盖 那么标记也会打两次以上

然后显然连边的时候 选取的路径上的边都不能被环覆盖过
所以原图就被环切成了很多树

对于每一个树 考虑dp
dp方式参考orz
最后的式子
\(f[u]=\prod g[v] \times h[num]\)
\(g[u]=f[u]+\prod g[v] \times h[num-1] \times num\)

多数据注意初始化复杂度!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
const ll P = 998244353;
const int inf = 0x3f3f3f3f;
struct Edge{int v, next;}edge[N << 2];
int head[N], esize;
inline void addedge(int x, int y){
	edge[++esize] = (Edge){y, head[x]}, head[x] = esize;
}
int n, m, fa[N], dfn[N], tag[N], a[N], dfncnt; ll ans, h[N], g[N], f[N];
inline bool rule(int x, int y){return dfn[x] < dfn[y];}
inline void init(){
	if(n){
		for(int i = 1; i <= n; ++i) head[i] = -1, tag[i] = dfn[i] = fa[i] = 0;
	}
	else {
		memset(head, -1, sizeof(head)), memset(tag, 0, sizeof(tag)),
    	memset(dfn, 0, sizeof(dfn)), memset(fa, 0, sizeof(fa));
	} 
	esize = 1, dfncnt = 0, ans = 1;
}
void dfs(int x, int ff){
	dfn[x] = ++dfncnt, fa[x] = ff;
	for(int i = head[x], vv; ~i; i = edge[i].next)
	    if(!dfn[edge[i].v]) dfs(edge[i].v, x); 
}
void update(int x){
	ll son = 0, gsum = 1; f[x] = 1, tag[x] = -1;
	for(int i = head[x], vv; ~i; i = edge[i].next){
		vv = edge[i].v; if(tag[vv] != 1 || vv == fa[x]) continue; 
		update(vv);  ++son, gsum = gsum * g[vv] % P;
	}
	f[x] = gsum * h[son] % P, g[x] = (f[x] + gsum * h[son - 1] % P * son % P) % P;
//	printf("%d %lld %lld\n", x, f[x], g[x]);
}
int main(){
	int T; scanf("%d", &T);
	h[1] = h[0] = 1, h[2] = 2;
	for(int i = 3; i <= 5e5; ++i) h[i] = (h[i - 1] + h[i - 2] * (i - 1) % P) % P;
	while(T--){
	    init();
		scanf("%d%d", &n, &m); 
		for(int i = 1, x, y; i <= m; ++i)
			scanf("%d%d", &x, &y), addedge(x, y), addedge(y, x);
		dfs(1, 0); bool flag = 0;
		for(int i = 2, uu, vv; i <= esize; i += 2){
			uu = edge[i ^ 1].v, vv = edge[i].v;
			if(dfn[uu] < dfn[vv]) swap(uu, vv);
			while(uu != vv){
				++tag[uu]; 
				if(tag[uu] > 2) {flag = 1; break;}
				uu = fa[uu];
			}
			if(flag) break;
		}
		if(flag){printf("0\n"); continue;}
		for(int i = 1; i <= n; ++i) a[i] = i;
		sort(a + 1, a + n + 1, rule);
		for(int i = 1; i <= n; ++i) if(~tag[a[i]]){
			update(a[i]); ans = ans * f[a[i]] % P;
		}
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2019-05-28 21:58  hjmmm  阅读(205)  评论(0编辑  收藏  举报