题解ZJOI2017仙人掌
Posted twilight-sx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解ZJOI2017仙人掌相关的知识,希望对你有一定的参考价值。
感觉这题很厉害啊,虽然想了一天多但还是失败了……(;д;)
这题首先注意到给定图中如果存在环其实对于答案是没有影响的。然后关键之处就在于两个 (dp) 数组,其中 (f[u]) 表示以 (u) 为根的子树中能构成仙人掌的方案数, 而 ( g[x] ) 则表示 (x) 个节点之间两两相互搭配(可以不搭配)的总方案数。转移则为:
(f[u] = prod f[v] * g[tot + [u != root]])
其中 (v) 为 (u) 的儿子节点,而 (tot) 表示 (u) 的总儿子个数。为什么这样做是对的呢?我也感到非常的困惑。之前自己在思考的时候其实有一个问题一直难住我:一个节点的儿子之间可以相互连边,这怎样处理?但此时我们将这些方案巧妙地连接在了一起。我们可以默认为求出来的 (f[u]) 中的方案数均为有一条边连向外界的方案。当这个方案匹配到另一子树的一种方案上的时候,表示这两条连向外界的边连接在了一起。若有没有匹配的,说明这条边没有连出去或连向根节点(若连向根节点且该点为儿子节点则说明没有连出去),但一样是合法的。
非常的厉害啊~其实感觉自己现在各种知识储备都还算可以了,但就是不够大胆,不能勇敢的提出一些想法和设想。一定要努力放开自己的思维,先猜测,再证明~
#include <bits/stdc++.h> using namespace std; #define maxn 1000000 #define mod 998244353 #define int long long int n, m, dep[maxn], g[maxn]; int timer, dfn[maxn], f[maxn]; int fa[maxn], mark[maxn], tot; int cnt, ans; struct edge { int cnp, head[maxn], to[maxn], last[maxn]; edge() { cnp = 1; } void add(int u, int v) { to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++; to[cnp] = u, last[cnp] = head[v], head[v] = cnp ++; } }E1; struct node { int id, dep; }a[maxn]; bool cmp(node a, node b) { return a.dep < b.dep; } int read() { int x = 0, k = 1; char c; c = getchar(); while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) k = -1; c = getchar(); } while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar(); return x * k; } void pre() { g[0] = g[1] = 1; for(int i = 2; i < maxn; ++ i) g[i] = (g[i - 1] + (i - 1)*g[i - 2]) % mod; return; } void Tarjan(int u) { dfn[u] = ++ timer; for(int i = E1.head[u]; i; i = E1.last[i]) { int v = E1.to[i]; if(dfn[v]) continue; fa[v] = u; dep[v] = dep[u] + 1; Tarjan(v); } return; } void dfs(int u, int rt) { mark[u] = -1; f[u] = 1; int tot = 0; for(int i = E1.head[u]; i; i = E1.last[i]) { int v = E1.to[i]; if(v == fa[u] || mark[v] != 1) continue; tot ++; dfs(v, 0); f[u] = f[u] * f[v] % mod; } if(!rt) f[u] = f[u] * g[tot + 1] % mod; else f[u] = f[u] * g[tot] % mod; return; } void Work() { n = read(), m = read(); E1.cnp = 2; for(int i = 1; i <= n; i ++) mark[i] = fa[i] = dep[i] = dfn[i] = E1.head[i] = 0; for(int i = 1; i <= m; i ++) { int u = read(), v = read(); E1.add(u, v); } dep[1] = 1; Tarjan(1); for(int i = 1; i <= m; i ++) { int u = E1.to[i << 1], v = E1.to[i << 1 | 1]; if(dfn[u] < dfn[v]) swap(u, v); while(u != v) { if(mark[u] == 2) { printf("0 "); return; } mark[u] ++; u = fa[u]; } } for(int i = 1; i <= n; i ++) a[i].id = i, a[i].dep = dep[i]; sort(a + 1, a + n + 1, cmp); ans = 1; for(int i = 1; i <= n; i ++) { int x = a[i].id; if(mark[x] == -1) continue; dfs(x, 1); ans = ans * f[x] % mod; } printf("%lld ", ans); return; } signed main() { pre(); int T = read(); while(T --) Work(); return 0; }
以上是关于题解ZJOI2017仙人掌的主要内容,如果未能解决你的问题,请参考以下文章