没用二项式反演的菜比。

题目链接

Solution

非平局代表的树上祖先关系是比较好统计,(可以在处理一个点时,考虑用他去匹配他的子树中的东西)而平局的关系比较难统计。我们不妨求出至少 \(k\) 个祖先关系的方案数,接着用容斥原理得到恰好 \(k\) 个祖先关系的方案数。

求出至少 k 个祖先关系的方案数

状态设计

设 \(f_{u, i}\) 为以 \(u\) 为根的子树中,已经有了 \(i\) 对相互配对的祖先点的方案数。

状态转移

  • 这个状态是由 \(u\) 的每个儿子与 \(u\) 的影响共同作用的,不妨先把 \(u\) 的每个儿子都合并起来,最后考虑 \(u\) 对状态的影响。

  • 设之前已经合并过子树的数组是 \(f_u\),当前的儿子是 \(v\),我们只需合并 \(f_u\) 和 \(f_v\) 即可,即 \(f_{u, i + j} \gets \sum f_{u, i} \times f_{v, j}\),可以理解为一个树上背包,体积是相互配对的祖先点对数,价值的方案数。

  • 考虑 \(u\) 的作用,让 \(u\) 和子树中的一个没有被匹配的点匹配,设 \(u\) 的颜色是 \(C_u \in {0, 1}\)。设 \(u\) 的子树中 \(0 / 1\) 颜色的点数量是 \(Size_{u, 0/1}\),\(!C_u\) 表示和 \(C_u\) 的颜色相反。那么 \(f_{u, i} \gets f_{u, i - 1} \times (Size_{u, !C_u} - (i - 1))\)(这一步很妙,无需记录别的信息就可以直接让 \(u\) 做到匹配,原因是通过已经匹配对数以及我们处理的树上信息可以计算出能匹配的点数)

答案

设 \(g_i\) 为整棵树中,至少有 \(k\) 对祖先关系(钦定了 \(k\) 对,剩下的不知道是不是祖先)的方案数,有 \(g_i = C_m^i \times f_{1, i}\),即先选出 \(i\) 对,然后剩下的自由匹配。

注意这里的至少和我们常接触的那个“至少”可能不太一样,具体看后面容斥的解释。

DP 的时间复杂度

第一个合并乍一看是 \(O(n^3)\) 的,但其实如果卡 \(i\) 的上限,发现 \(i\) 这一维不会超过 \(Size_u\)(以 \(u\) 为根的子树大小),那么一次合并的时间是 \(Size_{之前子树的和} \times Size_v\),相当于两块间两两的有序点对,那么两个点只会在 \(LCA\) 带来一次合并的贡献,所以是 \(O(n^2)\) 的。

用容斥原理得到恰好 k 个祖先

设 \(Ans_i\) 为整棵树中,恰好有 \(k\) 对祖先关系的方案数,即我们的答案。

发现这个 \(g\) 有点奇怪,就比如说我恰好有三组匹配 \(Ans_3 = 1\) 为 \(1、2、3\) (三个祖先匹配关系的编号没有实际意义),但 \(g_2\) 可以有 \(1、2\) 被钦定,\(3\) 随机对成祖先、可以有 \(1、3\) 被钦定,\(2\) 随机对成祖先、可以有 \(2、3\) 被钦定,\(1\) 随机对成祖先三种。换句话说,\(Ans_3\) 对 \(g_2\) 的贡献是 \(C_3^2 \times Ans_3\),总结一下:

可以把 \(g_i\) 的所有方案数划分成这样的类别:恰好有 \(i, i + 1, i + 2, ...m\),而对于一个恰好为 \(j \ge i\) 组的一个方案,他对 \(g_i\) 的贡献有 \(C_j^i\),可以理解为从 \(j\) 个里面选出 \(i\) 个,作为 \(g_i\) 中实际选定的那 \(i\) 个,即:

\(g_i = \sum_{j=i}^{m} (m-i)! \times Ans_j\)

由于 \(C_i^i = 1\),将如上式子进行变换,可以得到一个关于 \(Ans_i\) 的表达式:

\(Ans_i = g_i - \sum_{j=i+1}^m C_j^i \times Ans_j\)

即我们只需要知道 \(g_i\) 和 \(m \ge j > i\) 的 \(Ans_j\),就能算出 \(Ans_i\)。

那么从大到小推一遍,\(Ans\) 就出来了。

总时间复杂度

\(O(n^2)\)

Code

代码中实现没有再新建 \(g\) 与 \(Ans\) 数组,而是直接在 \(f_{1}\) 数组上操作。

#include <iostream>
#include <cstdio> using namespace std; const int N = 5005, P = 998244353; typedef long long LL; // sz[i] 表示以 i 为根子树的大小,cnt[i][0 / 1] 与文中的 size[i][0 / 1] 意义相同
int n, m, f[N][N], sz[N], cnt[N][2], tmp[N]; int fact[N], infact[N]; char s[N]; int head[N], numE = 0; struct E{
int next, v;
} e[N << 1]; void inline add(int u, int v) {
e[++numE] = (E) { head[u], v };
head[u] = numE;
} int inline power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = (LL)res * a % P;
a = (LL)a * a % P;
b >>= 1;
}
return res;
} void dfs(int u, int fa) {
f[u][0] = 1;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if (v == fa) continue;
dfs(v, u);
for (int j = 0; j <= sz[u] + sz[v]; j++) tmp[j] = 0;
for (int j = 0; j <= sz[u]; j++)
for (int k = 0; k <= sz[v]; k++)
tmp[j + k] = (tmp[j + k] + (LL)f[u][j] * f[v][k]) % P;
for (int j = 0; j <= sz[u] + sz[v]; j++) f[u][j] = tmp[j];
sz[u] += sz[v], cnt[u][0] += cnt[v][0], cnt[u][1] += cnt[v][1];
}
sz[u]++;
if (s[u] == '0') cnt[u][0]++;
else cnt[u][1]++;
for (int i = sz[u]; i; i--)
f[u][i] = (f[u][i] + (LL)f[u][i - 1] * (cnt[u][s[u] == '0' ? 1 : 0] - (i - 1))) % P;
} int inline C(int a, int b) {
return (LL)fact[a] * infact[b] % P * infact[a - b] % P;
} int main() {
scanf("%d%s", &n, s + 1);
m = n / 2;
fact[0] = infact[0] = 1;
for (int i = 1; i <= m; i++) fact[i] = (LL)fact[i - 1] * i % P;
infact[m] = power(fact[m], P - 2);
for (int i = m - 1; i; i--) infact[i] = (LL)infact[i + 1] * (i + 1) % P;
for (int i = 1, u, v; i < n; i++)
scanf("%d%d", &u, &v), add(u, v), add(v, u);
dfs(1, 0);
for (int i = 0; i <= m; i++) f[1][i] = (LL)f[1][i] * fact[m - i] % P;
for (int i = m; ~i; i--)
for (int j = i + 1; j <= m; j++)
f[1][i] = ((f[1][i] - (LL)f[1][j] * C(j, i)) % P + P) % P;
for (int i = 0; i <= m; i++) printf("%d\n", f[1][i]);
return 0;
}

NOI Online #2 提高组 游戏的更多相关文章

  1. 洛谷 P6478 - [NOI Online #2 提高组] 游戏(二项式反演+树形 dp)

    题面传送门 没错这就是我 boom0 的那场 NOIOL 的 T3 一年前,我在 NOIOL #2 的赛场上折戟沉沙,一年后,我从倒下的地方爬起. 我成功了,我不再是从前那个我了 我们首先假设 A 拥 ...

  2. luogu P6570 [NOI Online #3 提高组]优秀子序列 二进制 dp

    LINK:P6570 [NOI Online #3 提高组]优秀子序列 Online 2的T3 容易很多 不过出于某种原因(时间不太够 浪了 导致我连暴力的正解都没写. 容易想到 f[i][j]表示前 ...

  3. [NOI Online 2021 提高组] 积木小赛

    思路不说了. 想起来自己打比赛的时候,没睡好.随便写了个\(HASH\),模数开小一半分都没有. 然后学了\(SAM\),发现这个判重不就是个水题. \(SAM\)是字串tire的集合体. 随便\(d ...

  4. [NOI Online #2 提高组]涂色游戏 题解

    题目描述 你有 1020 个格子,它们从 0 开始编号,初始时所有格子都还未染色,现在你按如下规则对它们染色: 编号是 p1 倍数的格子(包括 0号格子,下同)染成红色. 编号是 p2 倍数的格子染成 ...

  5. NOI Online #2 提高组 游记

    没 NOI Online 1 挂的惨就来写游记吧,不知道为啥 NOI Online 1 民间数据测得 60 分的 T1 最后爆零了... 昏昏沉沉的醒来,吃了早饭,等到 \(8:30\) 进入比赛网页 ...

  6. CCF NOI Online 2021 提高组 T2 积木小赛 (子序列自动机+后缀自动机,O(n^2))

    题面 Alice 和 Bob 最近热衷于玩一个游戏--积木小赛. Alice 和 Bob 初始时各有 n 块积木从左至右排成一排,每块积木都被标上了一个英文小写字母. Alice 可以从自己的积木中丢 ...

  7. NOI Online #3 提高组 T1水壶 题解

    题目描述 有 n 个容量无穷大的水壶,它们从 1∼n 编号,初始时 i 号水壶中装有 Ai 单位的水. 你可以进行不超过 k 次操作,每次操作需要选择一个满足 1≤x≤n−1 的编号 x,然后把 x ...

  8. NOI On Line 提高组题解

    (话说其实我想填的是去年CSP的坑...但是貌似有一道题我还不会写咕咕咕... 先写一下这一次的题解吧. T1:序列.题意省略. 两种操作.这种题要先分析部分分 给出了全部都是2操作的子任务. 发现A ...

  9. NOI Online #3 提高组 游记

    考的好就来写游记吧 2020.5.24 星期日 上一天晚上为了班里事物做 PPT 肝到 11:30,这比赛就打打玩玩.第二天醒来有点昏昏沉沉的感觉. 打开题面,一看 T1,好像是个性质极其简单的前缀和 ...

随机推荐

  1. 为什么人们总是认为epoll 效率比select高!!!!!!

    今天看公司代码时,发现代码里面使用的事清一色的代码使用epoll, 所以就得说一说了:宏观看一看epoll 和select的实现: select原理概述 调用select时,会发生以下事情: 从用户空 ...

  2. 使用webhook watch pod

  3. git bash: error: RPC failed; result = 18, HTP code = 200B

    git config --global http.postBuffer 2428800 如果还是失败,说明buffer不够大,继续增加buff git config --global http.pos ...

  4. bluestore对象挂载到系统进行提取

    前言 之前在filestore里面,pg是直接暴露到文件系统的,也就是可以直接进去查看或者拷贝,在极端情况下,多个osd无法启动,pg无法导出的时候,那么对pg内部对象的操作处理,是可以作为最后恢复数 ...

  5. appium-appium的等待时间

    #三种appium设置等待时间的方法 #第一种 sleep(): 设置固定休眠时间. python 的 time 包提供了休眠方法 sleep() , 导入 time包后就可以使用 sleep()进行 ...

  6. loadrunner 生成随机参数 Radom相关

    我也是刚开始进入测试行业,不过比较幸运的我之前做过开发,所以对代码比较熟悉,对loadrunner没有进行过系统的学习,也是通过自己的摸索慢慢的积累知识. 今天遇到项目中要我做一个压力测试,其中一些参 ...

  7. 放弃腾讯75W年薪,回老家当公务员,提离职被领导教育。网友:leader嫉妒了

    最近一位腾讯员工自爆,"老家公务员政审已过,放弃腾讯75w年薪,提了离职被leader教育了".并且这位员工还晒出了领导"教育"自己的聊天记录,引发网友们的热议 ...

  8. 4.Spring Boot web开发

    1.创建一个web模块 (1).创建SpringBoot应用,选中我们需要的模块: (2).SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来 (3).自己编 ...

  9. Elasticsearch 国内镜像下载站

    镜像地址:https://thans.cn/mirror/elasticsearch.html 支持 5.0.0~7.3.1 各个平台的各个版本. 本文章转载他人.

  10. Redis实现分布式缓存

    Redis 分布式缓存实现(一) 1. 什么是缓存(Cache) 定义:就是计算机内存中的一段数据: 2. 内存中数据特点 a. 读写快    b. 断电立即丢失 3. 缓存解决了什么问题? a. 提 ...