嘘~ 正在从服务器偷取页面 . . .

[ARC101E] Ribbons on Tree 题解


[ARC101E] Ribbons on Tree 题解

题目链接:[ARC101E] Ribbons on Tree

题意

给定一个大小为 $n(2\le N\le 5000)$ 的树,保证 $n$ 为偶数。

您需要给树上的点两两配对,对于一组点对 $(u,v)$,在树上将 $u\leadsto v$ 的路径染色。

定义一个配对方案合法当且仅当所有边都有颜色,求方案数对 $10^9+7$ 取模。

输入:第一行 $n$ ,接下来 $n-1$ 行 $u,v$ 表示无向边 $u,v$ 。输出:一行答案。

先考虑一个 $\mathcal{O}(n^3)$ 的 dp。

设 $f(u,i)$ 为 $u$ 子树中有 $i$ 个点要向上匹配的方案数。

那么这就是一个树上背包,转移考虑枚举 $u$ 子树内的点数 $i$ , $v$ 子树内的点数 $j$ 和匹配的对数 $k$

答案就是 $f(1,0)$ 。不过这样做似乎不好优化。

正难则反,考虑计算不合法的方案数。

首先设 $g(n)$ 为 $n$ 个点两两匹配的方案数,其中 $n$ 为偶数,边界 $g(0)=1$ ,则

这个式子很好推,不过还是讲一下吧。

cxy 颇有大数学家费马的风范

记 $n=2k$ ,我们把点标上序号,分为奇偶两列,每次考察 $2k-1$ 和谁匹配。

  • 如果 $2k-1$ 和某个偶点匹配,那会有一个奇点失配并选择剩下那个偶点,方案数为 $k$ 。
  • 如果 $2k-1$ 和某个奇点匹配,那会有一个偶点失配并选择和 $2k$ 匹配,方案数为 $k-1$ 。

那么 $g(n) = (n-1) \times g(n-2) = \prod_{1 \le i \le \frac{n}{2}} (2i-1)$ ,就推出来了。

现在重新设 $f(u,i)$ 为 $u$ 子树内,$u$ 的合法连通块大小为 $i$ 时的方案数。

边界 $f(u,1) = 1$ 。初值是

转移钦定 $(u,v)$ 有没有颜色即可

这里 $a\downarrow b$ 表示 $a \gets a - b$ 。

最后答案就是

时间复杂度 $\mathcal{O}(n^2)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
const int mod = 1e9 + 7;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
void add(int &x, int y) { (x += y) >= mod ? x -= mod : 0; }
#define N ((int)(5e3 + 15))
#define addE(u, v) vec[u].push_back(v)

vector<int> vec[N];
int n, f[N][N], g[N], sz[N], tmp[N];
void dfs(int u, int fa)
{
    sz[u] = f[u][1] = 1;
    for(int v : vec[u]) if(v != fa)
    {
        dfs(v, u);
        for(int j = 1; j <= sz[u]; j++) { tmp[j] = f[u][j], f[u][j] = 0; }
        for(int j = 1; j <= sz[u]; j++)
            for(int k = 1; k <= sz[v]; k++)
            {
                add(f[u][j + k], tmp[j] * f[v][k] % mod);
                add(f[u][j], mod - tmp[j] * f[v][k] % mod * g[k] % mod);
            }
        sz[u] += sz[v];
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n; g[0] = 1;
    for(int i = 2; i <= n; i++) g[i] = g[i - 2] * (i - 1) % mod;
    for(int i = 1, u, v; i < n; i++)  { cin >> u >> v; addE(u, v); addE(v, u);}
    dfs(1, 0); int res = 0;
    for(int i = 1; i <= n; i++) add(res, f[1][i] * g[i] % mod);
    return cout << res << '\n', 0;
}

参考文献

[1] https://www.luogu.com.cn/article/to1ph9za


题外话

放图片,放图片。


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录