2021上海ICPC Edge Groups(树形dp,组合数学)
题目链接
题意:给一颗树,把所有n-1条边两两分组,每组的两条边要有公共节点,求方案数
思路:首先定义 d p [ u ] dp[u] dp[u]和 s i z [ u ] siz[u] siz[u],代表以u为根的时候产生的方案数,与以u为根节点的子树会提供多少个贡献,贡献的定义是有多少个子树的节点是偶数个。然后可以观察到,如果对于一个单独的子树,当节点个数为奇数的时候,只能够产生一种组合方式,因为这个时候子树内部的边是偶数,要全部配对的话就只有一种组合方式,只有节点个数是偶数的时候,才会对父节点提供新的组合方式,因为这个时候会有一条边是独立出来的。所以当 s i z [ v ] m o d 2 = = 0 siz[v] mod 2 == 0 siz[v]mod2==0的时候 s i z [ u ] siz[u] siz[u]才会+1,然后根据乘法原理, d p [ u ] ∗ = d p [ v ] dp[u]*=dp[v] dp[u]∗=dp[v]。最后因为有 s i z [ u ] 的 贡 献 siz[u]的贡献 siz[u]的贡献,他们可以选择两两组合,剩下得往上组合,所以最后还要遍历 s i z [ u ] siz[u] siz[u],把他累乘到 d p [ u ] dp[u] dp[u]上
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
const int MOD = 998244353;
int head[N], idx;
struct Edge{int to, nxt;}e[N << 1];
void add(int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
int siz[N], dp[N];
void dfs(int u, int fa)
{
dp[u] = 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v == fa) continue;
dfs(v, u);
dp[u] *= dp[v]; dp[u] %= MOD;
if (siz[v] % 2 == 0) siz[u]++;
}
for (int i = 1; i <= siz[u]; i += 2) dp[u] = dp[u] * i % MOD;
}
signed main()
{
int n;
cin >> n;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
add(u, v); add(v, u);
}
dfs(1, 0);
cout << dp[1];
return 0;
}