思路来自 某FXXL
不过复杂度咋算的..
/* HDU 6091 - Rikka with Match [ 树形DP ] | 2017 Multi-University Training Contest 5 题意: 给出N个点的树,求去边的方案数使得 去边后最大匹配数是M的倍数 限制: N<=5e4, M<=200 分析: 设 DP[u][i][0] 表示 以点 u 为根的子树 最大匹配数模 m 为 i 时,且 u 点没有匹配的方案数 DP[u][i][1] 表示 以点 u 为根的子树 最大匹配数模 m 为 i 时,且 u 点匹配上的方案数 得到对于 u 的某个子节点 v 对 u 的更新(讨论(u,v)的边连与不连) DP[u][k][0] += ∑ [i+j==k] 2 * DP[u][i][0] * DP[v][j][1] + 1 * DP[u][i][0] * DP[v][j][0] DP[u][k][1] += ∑ [i+j==k] 2 * DP[u][i][1] * ( DP[v][j][0] + DP[v][j][1] ) DP[u][k][1] += ∑ [i+j==k-1] DP[u][i][0] * DP[v][j][0] 每次在合并的时候更新u节点的取值范围,即 size[u] = min(size[u]+size[v], m) 这样复杂度大概 O(nm)(???) */ #include <bits/stdc++.h> using namespace std; #define LL long long const LL MOD = 998244353; const int N = 5e4+5; const int M = 205; vector<int> G[N]; int t, n, m; int size[N]; LL dp[N][M][2]; LL tmp[M<<1][2]; void solve(int u, int v) { memset(tmp, 0, sizeof(tmp)); for (int i = 0; i <= size[u]; i++) for (int j = 0; j <= size[v]; j++) { tmp[i+j][1] += 2 * dp[u][i][1] * (dp[v][j][0]+dp[v][j][1]); tmp[i+j][1] %= MOD; tmp[i+j+1][1] += dp[u][i][0] * dp[v][j][0]; tmp[i+j+1][1] %= MOD; tmp[i+j][0] += 2 * dp[u][i][0]*dp[v][j][1] + dp[u][i][0]*dp[v][j][0]; tmp[i+j][0] %= MOD; } for (int i = 0; i < m; i++) { dp[u][i][0] = (tmp[i][0] + tmp[i+m][0]) % MOD; dp[u][i][1] = (tmp[i][1] + tmp[i+m][1]) % MOD; } size[u] = min(m, size[u]+size[v]); } void dfs(int u, int pre) { memset(dp[u], 0, sizeof(dp[u])); dp[u][0][0] = 1; size[u] = 1; for (auto & v : G[u]) { if (v == pre) continue; dfs(v, u); solve(u, v); } } int main() { scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) G[i].clear(); for (int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dfs(1, 1); int ans = (dp[1][0][0]+dp[1][0][1]) % MOD; printf("%d ", ans); } }