Codeforces382E. Ksenia and Combinatorics
动态规划
(๑•́ ₃ •̀๑)卡题面的题真是恶心(打CF的那么多中国人,就是没有中文题面,哼(▔^▔))
最开始还以为要求最大匹配,想E题怎么这么简单,结果让你求方案。。
给你一棵树的节点个数 N (没有给你树的结构),然后让你求最大匹配为
M 的树有多少个,节点还有编号,编号不同也算不同的两棵树,答案对 109+7 取模
CF没有部份分,就算有,我也不知道这道题朴素怎么写,太难了,后来讨论下来才知道。
我先说说怎么求一棵树的最大匹配(神犇跳过这段),状态为
f[i][0/1]
,表示以
i
为根的树选根或者不选的最大匹配,转移方程为:
f[i][0]=∑f[u][1](u∈i的子节点) - f[i][1]=max(f[leftson][0]+f[rightson][1]+1,f[leftson][1]+f[rightson][0]+1)
那么根据这个我们给出这道题的状态:
f[i][j][k] 表示 i 个节点的树,不选根的最大匹配为j ,选根最大匹配为 k 的方案数,但是我们发现,选根最多只会比不选根多1,很好证明,就不多说,所以我们定义f[i][j][0/1] ,表示 i 个节点的树,不选根的最大匹配为j ,选根最大匹配是否多1的方案数,是否多1?所以我们就要枚举很多情况,因为还要考虑编号问题,所以需要用组合数来预处理一下。
下面是方程的转移(一些细节在代码里面能体现):-
f[i][j][0]=∑k=0i/2∑p=0k/2f[k][p][1]∗f[i−k−1][j−p][1]∗Cki−1∗k∗(i−k−1)
-
f[i][j][1]=∑k=0i/2∑p=0k/2(f[k][p][1]∗f[i−k−1][j−p−1][0]+f[k][p][0]∗f[i−k−1][j−p−1][1]
+f[k][p][0]∗f[i−k−1][j−p−1][0])∗Cki−1∗k∗(i−k−1)
这个转移还有很多细节,类似于左右儿子节点相同时要除以2(不能直接除,因为要取模,根据组合数的推倒间接除以2),当左儿子数等于0的时候就不要乘以0(即: Cki−1∗k∗(i−k−1) 要改为 Cki−1∗(i−k−1) ),所以,直接上代码(~o▔▽▔)~o:
#include <iostream> #include <cstdio> #include <cstdlib> using namespace std; typedef long long lld; const int maxn = 53, mod = 1e9 + 7; int n, m; lld f[maxn][maxn][2], C[maxn][maxn]; void initForComb() { C[0][0] = 1ll; for(int i = 1; i <= n; i++) { C[i][0] = 1ll; for(int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod; } } void add(lld &a, lld b, lld c, lld d) { b *= c; if(b >= mod) b %= mod; b *= d; if(b >= mod) b %= mod; a += b; if(a >= mod) a -= mod; } int main() { scanf("%d%d", &n, &m); if(n <= 1) { printf("0\n"); return 0; } initForComb(); f[1][0][0] = 1; f[0][0][1] = 1; for(int i = 2; i <= n; i++) { for(int j = 1; j <= i / 2; j++) { for(int k1 = 0, k2 = i - 1; k1 <= k2; k1++, k2--) { for(int p = 0; p <= j; p++) { lld CC; //避免算重复,当左儿子个数 = 右儿子个数时,要除以2 if(k1 != k2) CC = C[i - 1][k1] ; else CC = C[i - 2][k1 - 1]; if(k1) CC = CC * k1 % mod * k2 % mod; else CC = CC * k2 % mod; add(f[i][j][0], f[k1][p][1], f[k2][j - p][1], CC); add(f[i][j][1], f[k1][p][1], f[k2][j - p - 1][0], CC); add(f[i][j][1], f[k1][p][0], f[k2][j - p - 1][1], CC); add(f[i][j][1], f[k1][p][0], f[k2][j - p - 1][0], CC); } } } } printf("%lld\n", (f[n][m][0] + f[n][m][1]) % mod); return 0; }
-