前言

题目链接:洛谷

题目简述

给一棵树,问你这棵树的最大匹配是多少,并且计算出有多少种最大匹配。

题目分析

先来考虑较简单的最大匹配数。对于某一个结点,它有以下三种状态:

  1. 不参与匹配;
  2. 和某一个儿子匹配;
  3. 和父亲匹配。

考虑使用树形 DP,我们发现,只需要知道一个孩子,和或不和自己匹配的最大匹配是多少,也即,在 DP 时,前两种状态在向上合并时是相同的——都不能和父亲构成一个新的匹配。那我们就可以记 \(f[i][0 / 1]\) 表示 \(i\) 这个点,能或不能和父亲匹配构成的最大匹配数。为了方便,我们把一个匹配的贡献记在孩子上。那么答案 \(ans = f[root][0]\)。接下来考虑递推式。

以下考虑对于一个结点 \(yzh\) 和其一个孩子 \(xym \in \operatorname{son}(yzh)\)。

对于 \(f[yzh][1]\),即 \(yzh\) 可以和她父亲合并,那么她不能和她的孩子构成匹配。也就是只能从孩子的 \(f[xym][0]\) 转移而来。

\[f[yzh][1] = 1 + \sum f[xym][0]
\]

对于 \(f[yzh][0]\),即 \(yzh\) 可以不参与匹配,或者和某一个孩子构成匹配,相应转移即可。

\[\begin{aligned}
f[yzh][0] &= \max \Bigg \lbrace \sum f[xym][0], \sum f[xym][0] + \max \lbrace f[xym][1] - f[xym][0] \rbrace \Bigg \rbrace \\
&= \sum f[xym][0] + \max \lbrace f[xym][1] - f[xym][0] \rbrace \cup \lbrace 0 \rbrace
\end{aligned}
\]

对于方案数,我们再记 \(g[i][0 / 1]\),转移时把 \(\sum\) 变成 \(\prod\) 即可。

\[g[yzh][1] = \prod g[xym][0]
\]

如果 \(f[yzh][0]\) 在不和任何一个儿子匹配时取得 \(\max\),

\[g[yzh][0] = \prod g[xym][0]
\]

如果和某一个孩子 \(xym' \in \operatorname{son}(yzh)\) 匹配时取得 \(\max\),

\[g[yzh][0] = g[xym'][1] \prod _ {xym \neq xym'} g[xym][0]
\]

注意,如果有多个 \(xym'\) 满足,应根据加法原理对 \(g[yzh][0]\) 累加;如果 \(f[xym'][1] = f[xym'][0]\),在不参与匹配的方案数也要累加进来。

最后注意到题目最后一句话:

其中 \(40\%\) 的数据答案不超过 \(10^8\)。

我们要使用高精度存 \(g\) 的答案。有个 trick,\(\prod \limits _ {xym \neq xym'}\) 可以用前后缀积来优化。

代码

略去了高精度 BigInteger 的实现。

#include <vector>
#include <iostream>
#include <cstdio>
using namespace std; int n;
vector<int> edge[1010];
BigInteger g[1010][2];
int f[1010][2]; void dfs(int now) {
f[now][1] = 1, g[now][1] = 1;
unsigned mx = 0, son = 0;
for (unsigned i = 0; i < edge[now].size(); ++i) {
int to = edge[now][i];
++son, dfs(to);
f[now][1] += f[to][0], g[now][1] *= g[to][0];
if (f[to][1] - f[to][0] > f[edge[now][mx]][1] - f[edge[now][mx]][0])
mx = i;
}
if (!son) return g[now][0] = 1, void();
f[now][0] = f[now][1] - 1 + f[edge[now][mx]][1] - f[edge[now][mx]][0];
static BigInteger suf[1010]; suf[edge[now].size()] = 1;
for (unsigned i = edge[now].size() - 1; ~i; --i)
suf[i] = suf[i + 1] * g[edge[now][i]][0];
BigInteger pre = 1;
for (unsigned i = 0; i < edge[now].size(); ++i) {
int to = edge[now][i];
if (f[to][1] - f[to][0] == f[edge[now][mx]][1] - f[edge[now][mx]][0])
g[now][0] += pre * suf[i + 1] * g[to][1];
pre *= g[edge[now][i]][0];
}
if (f[edge[now][mx]][1] - f[edge[now][mx]][0] == 0)
g[now][0] += g[now][1];
} signed main(){
scanf("%d", &n);
for (int i = 1, u, v, k; i <= n; ++i) {
scanf("%d%d", &u, &k);
while (k--) scanf("%d", &v), edge[u].push_back(v);
}
dfs(1);
printf("%d\n", f[1][0]);
printf("%s", string(g[1][0]).c_str());
return 0;
}

后记 & 反思

一道很水的题目,但是模拟赛时没写出来,寄。

[CEOI2007] 树的匹配 Treasury 题解的更多相关文章

  1. luogu P1623 [CEOI2007]树的匹配Treasury

    题目链接 luogu P1623 [CEOI2007]树的匹配Treasury 题解 f[i][0/1]表示当前位置没用/用了 转移暴力就可以了 code // luogu-judger-enable ...

  2. [CEOI2007]树的匹配Treasury(树形DP+高精)

    题意 给一棵树,你可以匹配有边相连的两个点,问你这棵树的最大匹配时多少,并且计算出有多少种最大匹配. N≤1000,其中40%的数据答案不超过 108 题解 显然的树形DP+高精. 这题是作为考试题考 ...

  3. [CEOI2007] 树的匹配Treasury

    类型:树形 DP 传送门:>Here< 题意:给一棵树,你可以匹配有边相连的两个点,问你这棵树的最大匹配是多少,并且计算出有多少种最大匹配. 解题思路 首先树形Dp是很明显的,$f[i][ ...

  4. 【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索

    题目描述 求一棵 $[1,n]$ 的线段树的最大匹配数目与方案数. $n\le 10^{18}$ 题解 树形dp+记忆化搜索 设 $f[l][r]$ 表示根节点为 $[l,r]$ 的线段树,匹配选择根 ...

  5. 洛谷P2891 Dining P1402 酒店之王【类二分图匹配】题解+代码

    洛谷P2891 Dining P1402 酒店之王[类二分图匹配]题解+代码 酒店之王 题目描述 XX酒店的老板想成为酒店之王,本着这种希望,第一步要将酒店变得人性化.由于很多来住店的旅客有自己喜好的 ...

  6. bzoj5123 [Lydsy12月赛]线段树的匹配

    题意: 线段树是这样一种数据结构:根节点表示区间 [1, n]:对于任意一个表示区间 [l, r] 的节点,若 l < r, 则取 mid = ⌊l+r/2⌋,该节点的左儿子为 [l, mid] ...

  7. CF308C-Sereja and Brackets-(线段树+括号匹配)

    题意:给出一段括号,多次询问某个区间内能匹配多少括号. 题解:线段树,结构体三个属性,多余的左括号l,多余的右括号r,能够匹配的括号数val. 当前结点的val=左儿子的val+右儿子的val+min ...

  8. CodeForces 828E DNA Evolution(树状数组)题解

    题意:给你一个串k,进行两个操作: “1 a b”:把a位置的字母换成b “2 l r s”:求l到r有多少个字母和s匹配,匹配的条件是这样:从l开始无限循环s形成一个串ss,然后匹配ss和指定区间的 ...

  9. 51nod 1812 树的双直径 题解【树形DP】【贪心】

    老了-稍微麻烦一点的树形DP都想不到了. 题目描述 给定一棵树,边权是整数 \(c_i\) ,找出两条不相交的链(没有公共点),使得链长的乘积最大(链长定义为这条链上所有边的权值之和,如果这条链只有 ...

  10. 洛谷 P3374 【模板】树状数组 1 题解

    P3374 [模板]树状数组 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入格式 第一行包含两个整数N.M,分别表示该数列数字的个数 ...

随机推荐

  1. Linux安全审计之audit安装与使用

    场景 安全最重要的一步是内部安全,如何监控用户的行为是一个永恒不变的话题. audit可以详细监控用户的行为,详细到查看或修改了某个文件.这些都可以在日志中查看到. 安装 小贴士: CentOS默认已 ...

  2. HTTP协议 学习:1-报文分析

    HTTP协议 学习:1-报文分析 背景 上一讲我们介绍了HTTP协议的一些 概念 ,对HTTP协议有了一个基础的认识. 正如之前学习MQTT协议一样,我们需要对HTTP的报文进行分析. HTTP 报文 ...

  3. 手机护眼概论及OLED屏幕降低频闪原理介绍

    影响护眼的因素 蓝光 目前手机大多已经实现硬件低蓝光,而且蓝光也可以通过护眼模式轻易克服. 偏振光 偏振光指振动方向与传播方向不对称的光,主要分为圆偏振光与线偏振光两种. 线偏振光测试方法为:透过偏振 ...

  4. mysql求同比环比

    -- 参考:SQL计算月环比.月同比_路易吃泡面的博客-CSDN博客 -- mysql同比环比 drop table if EXISTS ordertable; create table ordert ...

  5. mysql 二进制的读取与写入

    插入语句 用binary转换函数可将字符串转为二进制 insert into mytable (id, bin) values(1, binary('abcdef')) 查询语句 用cast进行类型转 ...

  6. Flask API 如何接入 i18n 实现国际化多语言

    ​ 1. 介绍 上一篇文章分享了 Vue3 如何如何接入 i18n 实现国际化多语言,这里继续和大家分享 Flask 后端如何接入 i18n 实现国际化多语言. 用户请求 API 的多语言化其实有两种 ...

  7. 深度学习论文翻译解析(二十三):Segment Angthing

    论文标题:Segment Angthing 论文作者: Alexander Kirillov  Eric Mintun  Nikhila Ravi  Hanzi Mao... 论文地址:2304.02 ...

  8. [oeasy]python0072_整数类型_int_integer_整型变量

    帮助手册 回忆上次内容   上次了解的是 字符串 字符串 就是 字符的串 字符串长度 可以用 len函数 字符可以用下标索引 []   可以用str 将整型数字 转化为 字符串   字符的长度本身 有 ...

  9. CF1359A 题解

    洛谷链接&CF 链接 题目简述 共有 \(T\) 组数据. 对于每组数据给出 \(n,m,k\),表示 \(k\) 名玩家打牌,共 \(n\) 张牌,\(m\) 张王,保证 \(k \mid ...

  10. vs2019如何自动生成有下划线前缀的字段名?

    vs2019代码自动完成功能非常强大,今天要说的是根据构造函数的参数自动生成字段的事儿. 下图所示,IDE可以根据构造函数的参数自动生成私有字段 这个功能非常好,代码编写效率大大提升,生成的代码如下: ...