• 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;
    }
    
  • 相关阅读:
    django控制台输出sql日志
    Find概述
    命令大全
    京东智联云在 Serverless 的探索
    如何优雅地部署一个 Serverless Next.js 应用
    腾讯云 Serverless 保障《创造营》硬糖少女 C 位出道
    Serverless 应用实践及典型案例解析
    LeetCode 数组:62. 不同路径(动态规划 带记忆的递归)
    LeetCode 数组:56.合并区间(数组的自带排序函数 区间合并问题)
    LeetCode 数组:162. 寻找峰值(二分法)
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/9116046.html
Copyright © 2020-2023  润新知