AtCoder Beginner Contest 253 Solution

更好的阅读体验戳此进入

题面链接

题面 Luogu 链接

依然 abcd 太水了所以跳了

[ABC253E] Distance Sequence

题面

给定 n,m,k,求有多少序列 an,满足:

Solution

一看题意和数据范围,DP 显然,不难想到设状态 dp(i,j) 为长度为 i 的以 j 为结尾的合法序列的方案数。也不难想到一个 2D1D 的转移,即:

dp(i,j)=k=j+kmdp(i1,k)+k=1jkdp(i1,k)

同时注意转移前需要判断是否满足 j+kmjk1。然后这个式子也很显然可以前缀和优化成 2D0D,最后复杂度也就是 O(nm) 的。然后滚动数组也可以压掉一维,不过没必要,空间开的下。

然后按照这个做完会发现 WA 了两个点,具体看一下就会发现,按照这个式子,如果 k=0,那么 dp(i1,j) 就会被加两次,所以此时还需要特判一下减去一个 dp(i1,j)

Code

[ABC253F] Operations on a Matrix

题面

存在 nm 列的矩阵,给定 q 次操作,有 3 种格式。

Solution

感觉还算是一道细节不少的题。

首先这道题的做法不少,主流的就是类似二位偏序维护,或者写一个区间修改的主席树。

这里主要说一下用 BIT 维护的方法。

首先不难想到,2 操作会覆盖掉前面所有的对其有影响的 1 操作。然后 1 操作是区间修改列,2 操作是单点推平行。所以考虑离线,然后对于每个查询,令其序号为 r,有行 xy,我们要找到在其之前的上一个推平 x 行,令其的操作序号为 l,那么我们就需要对这个答案初始设为那次推平的值,然后加上 [l,r] 之间的所有的对于 y 列的操作。

思路就是这样,维护的方式还是有些高妙的。先考虑离线一遍,然后维护对于每个查询的上一次对应的推平,同时维护该查询的初始值,然后在需要减去的位置开个 basic_string 插进去需要减的序号。然后我们考虑会有一次区间的查询,用 BIT 和前缀和维护,再遍历一次操作,先将前缀加起来,然后减去的那个前缀就在我们第二次遍历的时候通过遍历对应的 basic_string 而减去。然后最后跑一遍输出答案即可。

Code

[ABC253G] Swap Many Times

题面

你有一个长度为 n 的序列 a,初始时,对于每一个 i(1in),满足 ai=i

然后你可以根据 n 构造出一个长度为 n(n1)2 的二元组序列,包含满足 1l<rn 的所有二元组 (l,r)。并且将得到的序列以 l 为第一关键字,r 为第二关键字由小到大排序。

例如,当 n=4,时,二元组序列为:

(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)

定义一个二元组 (l,r) 对应的操作为交换 alar

给出一个 L 和一个 R,要求你求出在执行完第 L 个到第 R 个二元组所对应的操作后的序列。

Solution

究极细节怪,大概就是手动模拟一下会发现规律,对于 l 相同的所有操作之后的结果就是将最后一个值换到 l 的位置,然后将 l 到最后的所有值向后推一次。然后类似分块地跑一下,左右散的分别考虑,中间的几块合在一起之后,就是将最后的一部分逆序放到前面,然后前面对应后移 siz 位,思路和性质都很简单,但是实现起来就特别恶心了,需要考虑大量细节。

Code

[ABC253Ex] We Love Forest

题面

给定 n 个点无边的图,给定 m 条待选的无向边。每次等概率地从 m 条边中抽取一条边加入图中。 n1 次询问求加 1,2,,n1 次边后原图形成一个森林(一棵树亦为森林)的概率为多少。对 998244353 取模。

Solution

考虑 FZT,即子集计数,具体可参考 FZT - 子集计数学习笔记[ABC213G] Connectivity 2 Solution,发现本题与 ABC213G 相似。

因为本题存在选的边数的限制,所以不难想到需要在一般 FZT 的状态中额外添加一维记录。

状态类比 ABC213G,较为显然,令 F(i,S) 表示选了 i 条边,点集状态为 S 形成的森林数。令 G(S) 表示点集状态为 S 生成树的总方案数。

首先考虑如何处理 G(S),发现这个东西就是个裸的矩阵树定理。不过也可以通过枚举子集设计转移来实现,令 cnt(S,T) 表示点集 S 和点集 T 之间的边数,方程不难想到为:

G(S)=TSG(T)×G(ST)×cnt(T,ST)

然后发现这个东西有重复,不难想到重复分为两种,一种是因为 TST 对调之后属于相同方案而被重复统计了,这个可以通过钦定一个点解决,或者在最终答案乘一个 12 解决。另一种是因为我们的转移,是通过将两个树形结构通过之间的边拼接在一起来实现的,而这种情况也可以认为是将一棵完整的树通过断开某一条边然后分别计算子树,这样的话对于一棵确定的树断开每条边都是等价的,所以会重复边数次,也就是答案需要乘一个 1|S|1,故可以考虑将转移改为:

G(S)=12(|S|1)TSG(T)×G(ST)×cnt(T,ST)

或:

G(S)=1|S|1lowbit(S)TSG(T)×G(ST)×cnt(T,ST)

然后初始值大概是 G(2k)=1

不难发现如果无脑处理 cnt 的话复杂度是 O(22nm),显然寄,于是考虑优化,令 cnte(S) 表示点集 S 中的边数,不难想到对于 cnt 可以如下容斥:

cnt(S,T)=cnte(ST)cnte(S)cnte(T)

则预处理 cnte 的复杂度为 O(m2n)cnt 可以 O(1) 求解,G 的处理则是 O(3n) 的。

然后矩阵树定理的结论大概就是首先对这个图建出拉普拉斯矩阵 L,然后答案就是 det(L0),其中 L0 表示 L 去掉第 i 行第 i 列的子矩阵,其中 i 任意。具体证明参考 矩阵树定理(Matrix-tree Theorem)笔记。所以只需要矩阵建出来无脑求一下行列式即可。

于是考虑如何转移 F,依然类比 ABC213G,我们令 |S| 表示点集 S 中点的数量,也就是 __builtin_popcount(S),不难发现转移:

F(i,S)=1TSG(T)×F(i(|T|1),ST)

也就是钦定一个子集为一棵树,然后剩下的作为森林,钦定 1 节点在树中可以去重,具体在 ABC213G 也有说明。

不过很遗憾这个又是错的。我们在 ABC213G 中钦定 1 是因为 1 一定被选,但是在本题里并没有这个限制,所以我们要换一种写法,不难想到使用 S 中的最后一个 1 是易于实现且正确的,即 lowbit(S),或者说 SS。于是转移改为:

F(i,S)=lowbit(S)TSG(T)×F(i(|T|1),ST)

边界条件的话就是如果 S 的点数恰好为 i+1,那么显然就是一棵树,即 F(i,S)=G(S)

然后考虑答案,令 Smx=2n1,即全集,不难想到对于选 i 条边的答案 ansi 有:

ansi=F(i,Smx)×i!mimod998244353

也不难理解,概率转计数,总方案数为 mi,然后要让所有点形成森林,方案中选择的 i 条边任意排列均不同,所以需要在乘一个 i!

 

大概就这样了,处理 G 的复杂度为 O(n32n),处理 F 的复杂度为 O(n3n),处理答案复杂度作为常数,最终复杂度 O((m+n3)2n+3n),可以通过。

Code

UPD

update-2022_12_07 初稿