Codefoces 382E Ksenia and Combinatorics - 动态规划

Posted yyf0309

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codefoces 382E Ksenia and Combinatorics - 动态规划相关的知识,希望对你有一定的参考价值。

题目传送门

  这是一个通往Codeforces的

  这是一个通往vjudge的

题目大意

  问以节点1为根的大小为$n$的带标号二叉树有多少个满足最大匹配数为$k$。答案模$10^{9} + 7$。

  树的最大匹配是指,选择尽量多的边,使得这些边没有公共点,最大匹配数是这个边集的大小。

  考虑二叉树的最大匹配怎么做。

  用$f[i]$表示$i$节点被覆盖的最大匹配数,$g[i]$表示$i$节点未被覆盖的最大匹配数。

  那么有$f[i] = \max (\max(f[l], g[l]) + f[r], \max (f[r], g[r]) + f[l]) + 1$,$g[i] = \max (f[l], g[l]) + \max (f[r], g[r])$。

  然后树形dp即可。

  因此可以设计出这里的状态$f[i][j][k]$表示$i$个点的满足条件二叉树,选根的最大匹配数为$j$,不选根的最大匹配数为$k$。

  但是这么做时间会炸掉。

  必须考虑优化状态。通过打表找规律其实是看了题解,可以发现一个神奇的事情。

神奇的性质 一棵二叉树,如果节点数大于1,那么根被覆盖要么比根未被覆盖的情况的最大匹配数多1要么和它相等。

  证明 要证明它,等价于证明$g[i] \geqslant f[i] - 1$以及$f[i] \geqslant g[i]$。

  首先考虑前一个不等式,对于一个根被覆盖的匹配,我只需去掉根和某个子节点的匹配,然后就变成了一个根未被覆盖的匹配,但是匹配数只比原来少一,所以$g[i] \geqslant f[i] - 1$。

  然后考虑后一个不等式,讨论$g[i]$对应的匹配中根节点的子树,因为节点数大于1,所以根至少存在一个子树。

  • 子树的根节点在匹配中。那么断开匹配它的边,让它和根节点之间的边被匹配。
  • 子数的根节点不在匹配中。那么让它和根节点质检的边匹配。

  这样就证明了$f[i] \geqslant g[i]$。

  因此,定理得证。

  因此,可以直接去掉$k$,把它变成$0 / 1$,表示根被覆盖的最大匹配数是否比根未被覆盖的最大匹配数多1。

  然后再来考虑一个问题:如何处理算重的情况?

  1. 钦定左子树大小小于等于右子树
  2. 当左子树大小等于右子树的时候,钦定2号点在左子树内。

  以上很多讨论都要求节点数大于2,所以节点数小于等于1的情况直接赋初值。

  然后考虑转移。

  枚举左子树大小,左右子树匹配数,转移的时候特判左子树是否为空,考虑从左右子树中重新选取一个点作为新根,以及左右子树各有哪些点。

  具体转移请看代码。

Code

 1 /**
 2  * Codeforces
 3  * Problem#382E
 4  * Accepted
 5  * Time: 16ms
 6  * Memory: 2056k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 const int N = 55, M = 1e9 + 7;
13 
14 int n, k;
15 int C[N][N];
16 int f[N][N][2];
17 
18 inline void init() {
19     scanf("%d%d", &n, &k);
20 }
21 
22 inline void solve() {
23     C[0][0] = 1;
24     for (int i = 1; i <= n; i++) {
25         C[i][0] = C[i][i] = 1;
26         for (int j = 1; j < i; j++)
27             C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % M;
28     }
29     
30     f[0][0][0] = 1, f[1][0][0] = 1;
31     for (int i = 2; i <= n; i++) {
32         for (int ls = 0, rs, c, f0, f1, cl; (ls << 1) < i; ls++) {
33             rs = i - 1 - ls;
34             c = ((ls == rs) ? (C[i - 2][ls - 1]) : (C[i - 1][ls]));
35             cl = ((!ls) ? (1) : (ls));
36             for (int lk = 0; (lk << 1) <= ls; lk++) {
37                 for (int rk = 0; (rk << 1) <= rs; rk++) {
38                     for (int j = 0, b1, b2, b; j < 4; j++) {
39                         b1 = (j & 1), b2 = ((j & 2) >> 1);
40                         if (!lk && b1)    continue;
41                         if (!rk && b2)    continue;
42                         f0 = lk + rk, f1 = (ls) ? (lk + rk - min(b1, b2) + 1) : (rk - b2 + 1);
43                         b = f1 - f0;
44                         f[i][f1][b] = (f[i][f1][b] + (f[ls][lk][b1] * 1ll * f[rs][rk][b2] % M * c) % M * cl * rs % M) % M;
45                     }
46                 }
47             }
48         }
49     }
50     printf("%d\n", (f[n][k][0] + f[n][k][1]) % M);
51 }
52 
53 int main() {
54     init();
55     solve();
56     return 0;
57 }

以上是关于Codefoces 382E Ksenia and Combinatorics - 动态规划的主要内容,如果未能解决你的问题,请参考以下文章

codefoces812C-Sagheer and Nubian Market心得

Codefoces514C - Watto and Mechanism(Tire)

[CF1438D] Powerful Ksenia - 构造

codefoces_#346E - New Reform(并查集或dfs)

Codefoces909E Coprocessor(拓扑排序)

Codeforces1438 D. Powerful Ksenia(构造,异或性质)