计数好题分享

Twenty-two - 题目 - QOJ.ac

给定 \(n,m,k\) 和一个长为 \(n\) 的正整数序列 \(a\),有 \(m\) 种一操作,给定 \(v\) 表示所有 \(a_i\)\(v\)\(\min\),有 \(k\) 种操作二,给定 \(l,r,v\) 表示 \(a_l,\ldots,a_r\)\(v\)\(\max\)。求任意安排这 \(m+k\) 次操作的顺序,最终能得到多少种不同的 \(a\)

答案对 \(998244353\) 取模。\(n,m,k\le 150\)

首先可以把对 \(a_i\) 的所有操作都看成给定区间 \([l,r]\),让 \(a_i\) 移到 \([l,r]\) 内。如果所有 \([l,r]\) 有交,设最终交为 \([L,R]\),那么无论操作顺序是什么 \(a_i\) 最终的值是确定的。否则,\(a_i\) 最终的值只与操作顺序有关,也就是说两种情况都与 \(a_i\) 无关,可以看成 \(a_i\) 初始全为 \(0\)

对于每个一操作,可以将之前的所有二操作的 \(v\) 都对自己的 \(v\)\(\min\),这样就只剩二操作了。可以发现一操作间按 \(v\) 从小到大依次操作一定最优,设一操作排序并去重后是 \(v_1,\ldots,v_m\),这样原来一个二操作 \((l,r,v)\) 就可能是 \((l,r,\min(v_1,v)),\ldots,(l,r,\min(v_m,v)),(l,r,v)\) 中的任意一个,而因为只有二操作所以不用关心操作之间的顺序。

继续转化,可以看成先做 \((l,r,\min(v_1,v))\) 操作,然后对每个 \((l,r,\min(v_2,v)),\ldots,(l,r,\min(v_m,v)),(l,r,v)\) 依次决定做不做。因为所有先做的操作都满足 \(v\le v_1\) 而之后想做就做的操作都满足 \(v>v_1\),所以能忽略这些先做的操作,相当于还是看成全 \(0\)。现在问题变为了,有一个初始全 \(0\) 的数组,有若干个 \((l,r,v)\) 操作,求选一个操作子集能得到多少不同的 \(a\)

考虑经典套路,设 \(f_{l,r,v}\) 表示只考虑 \([l,r]\) 内的操作,能得到多少个 \(a_l,\ldots,a_r\)\(\ge v\)\(a\)。转移时枚举 \([l,r]\) 内所有 \(=v\) 的位置 \(k_1,\ldots,k_p\),贡献为 \(f_{l,k_1-1,v+1}\times f_{k_1+1,k_2-1,v+1}\times \ldots\times f_{k+p+1,r,v+1}\),要求所有 \(k_i\) 都被至少一个 \([l,r]\) 内的 \((l',r',v)\) 覆盖。注意不能只枚举第一个位置 \(k_1\) 然后转移,因为后面的 \(k_i\) 有可能本来被一个区间覆盖但在 \([k_1+1,r]\) 中没有区间覆盖。复杂度 \(\mathcal{O}(n^5)\)

上述做法的瓶颈在于对每个 \((l,r)\) 都要做一次内层 dp,考虑优化,用容斥来做。设 \(g_{l,r,v}\) 表示不考虑 \(k_i\) 要被至少一个区间覆盖的限制的答案,转移为 \(g_{l,r,v} = f_{l,r,v+1}+\sum\limits_{k=l}^rg_{l,k-1,v}\times f_{k+1,r,v+1}\)。先将 \(f_{l,r,v}\) 赋为 \(g_{l,r,v}\),然后枚举第一个不满足条件的 \(k_i\),有 \(f_{l,r,v}\gets -f_{l,k-1,v}\times g_{k+1,r,v}\)。这样就将复杂度优化到了 \(\mathcal{O}(n^4)\)

提交记录 #2424664 - QOJ.ac

[ARC138E] Decreasing Subsequence

给定整数 \(n,k\),设满足以下条件的整数序列 \((a_1,\ldots,a_n)\) 为好序列:

  • \(\forall 1\le i\le n\)\(0\le a_i\le i\)
  • \(\forall 1\le i\le n\)\(i\) 在至多出现一次。

对所有好序列,求序列中长为 \(k\) 的下降子序列的个数之和,要求子序列只包含正整数。

答案对 \(10^9+7\) 取模。\(n,k\le 5000\)

加强版1:设上述问题答案为 \(f(n,k)\),求 \(f(1,k),\ldots,f(n,k)\)

加强版2:求 \(f(n,1),\ldots,f(n,k)\)

做法一:

因为每种数至多出现一次,考虑建图,对每个 \(i\) 连边 \(i\to a_i-1\),如果 \(a_i=0\) 则不连,那么会形成若干条链。如果求总方案数就是 \(0\sim n\) 这些点分成若干个无序集合,即 \(Bell_{n+1}\)。考虑一个长为 \(k\) 的下降子序列,相当于有如下 \(2k\) 个点,并且形成了 \(k\) 条链。

img

考虑分为左右两部分,枚举左边这 \(k\) 条链共包含 \(i\) 个点,右边共包含 \(j\) 个点,贡献为 \({i\brace k}{j\brace k}\),剩下 \(n+1-i-j\) 个点可以任意连,即最终答案为:

\[\sum_i \sum_j {n+1\choose i+j}{i\brace k}{j\brace k}Bell_{n+1-i-j} \]

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

做法二:

可以发现上面这个东西完全没法做不同的 \(k\)。考虑用 dp 去做,一个简单的想法为 \(f_{i,j}\) 表示前 \(i\) 个数,有 \(j\)\(\le i\) 的数没有选,转移是简单的。考虑如何去计算答案,枚举子序列开头下标 \(i_1\),分为前后两个部分,但这样枚举 \(i_1\) 后还要枚举前后分别有多少个 \(0\),复杂度是 \(\mathcal{O}(n^3)\) 的。

考虑换一个 dp 状态,如果我们知道 \(i_1\) 后面有多少个 \(\le i_1\) 的数,那么可以直接任选 \(k-1\) 个数,这些按顺序排列的概率为 \(\frac 1 {(k-1)!}\)。于是设 \(f_{i,j}\) 表示考虑了前 \(i\) 个数,后面的数中恰好有 \(j\)\(\le i\) 的正整数。转移时先考虑 \(i\) 这个数是否放到 \(i\sim n\) 中,然后再考虑 \(a_i\) 填啥,即:

\[f_{i-1,j}\to t_j,f_{i-1,j}\to t_{j+1} \\ f_{i,j}\gets t_j,f_{i,j-1}\gets t_j\times j \]

然后设 \(g_{i,j}\),状态定义与 \(f\) 相同,并且 \(i_1\le i\) 的贡献。对每个 \(i\) 考虑 \(i_1=1\) 的情况,有转移 \(g_{i,j-1}\gets t_j\times j\times \frac{j-1\choose k-1}{(k-1)!}\)\(g\) 的转移与 \(f\) 相同。这样总复杂度也是 \(\mathcal{O}(n^2)\)

最终 \(n\) 个数的答案就是 \(g_{n,0}\),可以完成加强版1。

考虑加强版2,将 \(g\) 的转移倒过来,对每个 \(j\) 求出所有 \(i\)\(f_{i,j}\times g_{i,j-1}\times t_j\) 之和。接下来再枚举每个 \(k\),对每个 \(j\)\(\mathcal{O}(1)\) 算出 \(j\)\(k\) 的贡献,于是也能做到 \(\mathcal{O}(n^2)\) 的复杂度。

Submission #75979494 - Daiwa Securities Co. Ltd. Programming Contest 2022 Spring (AtCoder Regular Contest 138)

CF2018F3 Speedbreaker Counting (Hard Version)

给定 \(n\)。对于一个长为 \(n\) 的正整数序列 \(a_i\),设 \(f(a)\) 为以下问题的答案:

  • \(n\) 个点排成一排,第 \(1\) 时刻你可以任意点亮一个点,接下来每一时刻都可以点亮一个与已经被点亮的点相邻的点,要求每个 \(i\) 的满足点亮第 \(i\) 点的时刻不超过 \(a_i\)。求有多少个不同的起点能完成上述操作。

对每个 \(0\le k\le n\),统计有多少个 \(a\) 满足:

  • \(\forall 1\le i\le n,1\le a_i\le n\)\(f(a) = k\)

答案对给定质数 \(p\) 取模。\(n\le 3000\)

加强版:\(n\le 10^7\)

考虑如何判断一个点是否合法,一个贪心策略为从小到大枚举 \(a_i\),每次去点亮 \(i\),这样一定是最优的。可以发现合法的起点一定构成一段区间,考虑如何判断 \([l,r]\) 内的点是否能都作为起点。

如果 \([l,r]\) 的点都能作为起点,那么从里面任意一个点开始一定是先点亮 \([l,r]\) 中的所有点,找到最小的 \(i\) 满足 \(i\not\in [l,r]\),因为从 \(l\)\(r\) 出发都要先点亮 \(i\),那么离 \(i\) 更远的那个端点一定点亮了整个区间,所以对于任意起点在这个时候点亮整个区间一定不劣。而区间内最严的限制为 \(l,r\) 两个端点,即要求 \(a_i\ge \max(i-l+1,r-i+1)\)

接下来考虑两端,这里也能像上面一样不断拓展,但这样不好计数。考虑倒着做,可以发现 \(a_1,a_n\) 中一定有个为 \(n\) 表示最后点亮这个点,那么删掉这个点后就递归到了子问题。每次相当于一定能从 \(a_i,a_j\) 中找到一个 \(j-i+1\),一直删到 \([l,r]\) 即可。

考虑计数 \(ans_{l,r}\) 表示 \(l\sim r\) 的点都能作为起点的答案,\([l,r]\) 内部的贡献是好算的。为了防止计重,我们钦定能删右边就删右边,设 \(f_{l,r,0/1}\) 表示删到了区间 \([l,r]\),上一步删的是左边/右边。那么 \(f_{l,r,0/1}\) 可以转到 \(f_{l+1,r,0},f_{l,r-1,1}\),转移要求 \(a_l\)\(a_r\) 不小于 \(r-l+1\),贡献为 \(n-(r-l+1)+1\)。但如果上一步删的是左边说面 \(a_r < r-l+2\),那么这次删右边 \(a_r\) 一定为 \(r-l+1\),贡献为 \(1\)。即只有在 \(0\) 转为 \(1\) 时贡献为 \(1\),其余为 \(n-(r-l+1)+1\)

初始化为 \(f_{1,n,1} = 1\)\(ans_{l,r}\)\(f_{l,r,0}+f_{l,r,1}\) 乘上内部 \(a_i\) 方案,这是好算的。最后因为我们是钦定的 \([l,r]\) 而不是恰好,所以要容斥,最终 \([l,r]\)\(res_{r-l+1}\) 的贡献为 \(ans_{l,r}-ans_{l-1,r}-ans_{l,r+1}+ans_{l-1,r+1}\)\(res_0\) 可以用总方案减去所有 \(res_i\) 得到。总复杂度为 \(\mathcal{O}(n^2)\)

Submission #374503372 - Codeforces

可以发现我们其实只关心区间长度 \(r-l+1\)。设 \(f_{i,0/1}\) 表示所有区间长为 \(i\) 的 dp 值之和,转移和上面一样。长为 \(i\) 的区间的内部贡献也可以递推求出。得到 \(ans_i\) 后最终 \(i\) 的答案为 \(ans_i-2ans_{i+1}+ans_{i+2}\),于是可以做到 \(\mathcal{O}(n)\) 的复杂度。

Submission #375289148 - Codeforces

[ARC089F] ColoringBalls

给定 \(n,k\) 和一个长为 \(k\) 的由 \(r,b\) 构成的字符串 \(s\),有 \(n\) 个白球排成一行,执行 \(k\) 次操作,每次选择一段区间(可以为空)染为颜色 \(s_i\)。注意每次染色会覆盖之前的颜色,但白球不能直接染为蓝色。求最终能得到多少不同的颜色序列。

答案对 \(10^9+7\) 取模。\(n,k\le 70\)

考虑怎么判定一种最终状态能否达到。首先对于每个红蓝连续段分开考虑,然后将每个颜色连续段缩成一个点,设每个红蓝连续段中有 \(k\) 个蓝色,那么一定先是 rb,然后 \(k-1\) 个任意字符,\(k=0\) 就是一个 r。那么可以贪心判断,设有 \(x\) 个含蓝色的连续段和 \(y\) 个纯红段,那么找出前 \(x\) 个 r 和每个 r 后面第一个 b,然后再另外找出前 y 个 r,那么这些位置就是段的开头,不能选。

接下来从前往后考虑每个 rb 组合,设 \(a_1,\ldots,a_x\) 表示每个段的 b 个数,显然我们从大到小填最优。设 \(sum_i\) 表示从后往前第 \(i\) 个 rb 组合的 b 后面有多少个字符可以选,然后将 \(a_i\) 从小到大排序,那么合法的充要条件就是 \(\forall 1\le i\le x\),有 \(\sum\limits_{j=1}^i a_j-1\le sum_i\)。考虑计数,首先外层枚举 \(x,y\),需要划分出 \(x\) 个 rb 组合和剩下 y 个 r 否则无解。

因为我们要判定,所以肯定是枚举 \(a_i\) 排序后的结果,考虑枚举 \(a_i\) 后有多少种原来的可能。首先我们计算的是 \(a_i\) 的可重集,于是贡献有一个 \(a_i\) 的可重集计数。考虑剩下的字符,假设总共有 \(i\) 个 b,那么一定有的字符为 \(x\)\(br.\ldots b\)\(y\)\(r\) 以及这 \(x+y\) 段之间的白色,共 \(2i-x+y+x+y-1\),剩下的数可以放进 \(2i+x+y+x+y-1\) 个空位中,可以直接组合数计算。

\(f_{i,j}\) 表示放了 \(i\) 段,总共有 \(\sum a_i-1\)\(j\) 的方案数,从小到大枚举 \(v\)\(v\) 的出现次数 \(c\) 转移即可。单次复杂度为 \(\mathcal{O}(n^3\ln n)\),总复杂度为 \(\mathcal{O}(n^5\ln n)\),常数很小可以通过。

Submission #71193513 - AtCoder Regular Contest 089

AT_wtf22_day2_d Cat Jumps

给定长为 \(n\) 的正整数序列 \(a\),设 \(S = \sum a_i\),共 \(n+S\) 张卡片,其中 \(n\) 张分别写着 \(a_i\)\(S\) 张写着 \(-1\)。对于这些卡片的一种排列,定义其得分为 \(1\sim n+S\) 中有多少个前缀和为 \(0\),对每个 \(k=1\sim n\) 求出有多少个本质不同的排列得分为 \(k\)

答案对 \(998244353\) 取模。\(n\le 5000,a_i\le 10^9\)

首先将所有 \(a_i\) 看成有相对顺序的,然后最后将答案除以每个数出现次数的阶乘。(注意 \(-1\) 之间仍然没有相对顺序)

然后因为是要求恰好 \(k\) 个硬币,我们可以求钦定 \(k\) 个硬币的方案,再使用二项式反演即可求得恰好的方案。一个序列是恰好获得 \(k\) 个硬币的,相当于可以将序列划分为 \(k\) 段,满足每一段都是和为 \(0\) 的极小连续段,即不能再划分下去。

\(f_k\) 表示钦定 \(k\) 个硬币的答案,就是将序列划分为了 \(k\) 段满足每一段的和为 \(0\),但不用满足每一段都是极小的。于是我们可以枚举将 \(n\) 个数划分为 \(k\) 个集合的方案,那么对于一个集合 \(S\),相当于把集合 \(S\) 中的 \(a_i\) 划分为了一段。设 \(sum = \sum\limits_{i\in S}a_i\),这一段中就有 \(sum\)\(-1\),于是这一段的方案数为 \((sum+1)(sum+2)\ldots(sum+|S|)\)。这一个划分方案的贡献为所有段的方案数的乘积再乘上 \(k!\)

注意最后二项式反演时容斥系数为 \((-1)^{j-i} {j-1\choose i-1}\)。这个做法复杂度为 \(\mathcal{O}(B_nn)\),其中 \(B_n\) 表示贝尔数,即 \(n\) 个数的集合划分方案,也等于 \(\sum\limits_{i=0}^n{n\brace i}\)

继续优化,考虑这个式子的组合意义,相当于一个完全图,\(i\)\(j\) 有边权为 \(a_j+[j\le i]\) 的边,一种划分方案的贡献为每个点向同一个集合内的其中一个点连边,所有连边方案的边权的乘积之和。因为一个集合内编号第 \(i\) 小的点向集合内所有点连边的边权和为 \(sum+i\)

那么最终连出来的图是一个内向基环树森林,假设每个点可以向任意点连边,设 \(g_k\) 表示最终有 \(k\) 个基环树的方案,那么一个 \(g_i\)\(f_j\) 的贡献为将这 \(i\) 个基环树划分为 \(j\) 个集合的方案数,即 \(i\brace j\)。所以求出 \(g_i\) 后就能通过 \(f_i = \sum\limits_{j=i}^n g_j{j\brace i}\) 来求出 \(f_i\)

现在考虑求 \(g_k\),我们可以钦定有多少个环,设钦定有 \(k\) 个环的所有图的权值和为 \(h_k\),对 \(h\) 进行一次二项式反演即可得到 \(g\)。现在假设我们钦定了 \(k\) 个环,那么环外的点就可以向任意点连边,所以一个环外的点 \(x\) 的贡献为 \(x+\sum\limits_{i=1}^na_i\)。环上的边权仍然是 \(a_j+[j\le i]\),我们可以把这个式子写成 \((a_j+1)-[j>i]\)

考虑组合意义,因为是所有边权乘起来,所以相当于在每个 \((a_j+1)-[j>i]\) 的式子中选择其中一项。那么对于一段选择了 \(-1\) 的连续段,就对应了一条编号递增的链。换句话说,如果选择了若干条编号递增的链,就有每条链的链头 \(j\) 的贡献为 \(a_j+1\),其余点的贡献为 \(-1\),然后再将这些链拼成 \(k\) 个环。假设我们求出了选择 \(i\) 条链的权值和,那么拼成 \(j\) 个环的方案数相当于将 \(i\) 条链划分为 \(j\) 个集合,每个集合 \(S\) 的贡献为 \((|S|-1)!\),可以发现这就是 \({i\brack j}\)。于是我们求出选择 \(k\) 条链的权值和之后,乘第一类斯特林数即可得到 \(h_k\)

现在考虑求选择 \(k\) 条链的权值和。设 \(dp_{i,j}\) 表示编号前 \(i\) 的点,拼成 \(j\) 条链的权值和,那么对于第 \(i\) 个点分三种情况讨论:

  • 单独成一条链,链头贡献为 \(a_i+1\)\(dp_{i,j}\gets dp_{i-1,j-1}(a_i+1)\)
  • 接在某一条链的后面,贡献为 \(-1\)\(dp_{i,j}\gets dp_{i-1,j}\times (-j)\)
  • 放在环外,贡献为 \(i+\sum\limits_{j=1}^n a_j\)\(dp_{i,j}\gets dp_{i-1,j}(i+\sum\limits_{j=1}^n a_j)\)

于是整个问题可以在 \(\mathcal{O}(n^2)\) 内求出。

Submission #66055576 - World Tour Finals 2022 Day2(Open Contest)

AT_wtf22_day1_c Shrink the Tree

给定一棵 \(n\) 个点的无根树,点编号为 \(1\sim n\),每次可以选择两个距离为奇数的叶子节点删去,求进行若干次操作后剩余点集有多少种可能。

答案对 \(998244353\) 取模。\(n\le 150\)

首先将整棵树黑白染色,那么每次操作为选择一黑一白两个叶子节点删去。考虑有若干棵树 \(u_1,\ldots,u_k\),这些树之间能被删空的条件是什么。单独考虑任意一棵树,将这棵树删空可能需要若干个外部的点,可以发现我们只需要关心最少需要用多少个外部的点,因为可以将任意一次内部的删除改为和两个外部点删除。

\(f_u\) 表示要删空子树 \(u\) 至少要多少个外部点,那么删空 \(u\) 可以用 \(f_u,f_u+2,\ldots,siz_u\) 个外部点。考虑删完 \(u_1,\ldots,u_k\) 有哪些必要条件:

  • \(\sum b_{u_i} = \sum w_{u_i}\),其中 \(b_u,w_u\) 分别表示 \(u\) 子树内黑点、白点个数,显然删空要求黑白点数相等。
  • \(\forall i,f_{u_i}\le \sum\limits_{j\ne i}siz_{u_j}\),即每棵树要有足够多的外部点来删,也可以写成 \(f_{u_i}+siz_{u_i}\le \sum siz_{u_j}\)
  • 所有根节点不同色。

并且我们也能证明这些条件也是充分的,考虑归纳法,即每次都可以删去一对点使得剩下的树依然满足条件:

  • 所有叶子节点不可能全部同色,如果全部同色,不妨设为白色,则所有子树都有 \(w_{u_i}\ge b_{u_i}\),又因为 \(\sum b_{u_i} = \sum w_{u_i}\) 所以每棵子树黑白点数相等,那么每棵子树根节点都为黑色,与第三个条件矛盾。

  • 找到 \(siz_{u_i}\) 最大的 \(u_i\),那么删去这棵树中的一个叶子和其余某个叶子一定还是满足前两个条件。如果 \(u_i\) 叶子节点不同色,则直接删一对叶子即可,否则不妨设叶子节点全为白色,则 \(w_{u_i}\ge b_{u_i}\)。找到存在黑点叶子且 \(siz\) 最大的一棵子树,与 \(u_i\) 的某个白色叶子删除即可。

  • 考虑反证法,如果按上述做法删掉后根节点同色了,那么只有可能是删掉了唯一一个单独的黑点,其余树全部为白色根节点且叶子全白,但这样的树一定满足白点多余黑点,所以只有可能只存在 \(u_i\) 和这个黑点。这种情况下 \(u_i\) 子树至少要两个外部点,与第二个条件矛盾。所以每次一定可以找到一对可以删的叶子。

接下来考虑求 \(f_u\),首先点 \(u\) 一定要跟一个点消掉,剩下问题为至少要在 \(u\) 上挂多少个叶子,才能将 \(u\) 所有儿子子树消完。首先如果 \(u\) 没有儿子是简单的,为满足第一个条件,\(f_u\) 至少为 \(|b_u-w_u|\),为满足第三个条件,\(f_u\) 至少为 \(\max f_v-(siz_u-1-siz_v)\)。如果 \(f_u\) 取到了 \(|b_u-w_u|\) 并且少的颜色恰好是所有 \(v\) 的颜色,说明此时所有儿子子树的根节点颜色都相同,那么还要额外补 \(2\) 个点。

于是我们在 \(\mathcal{O}(n)\) 内求出了以某个点为根时所有子树的 \(f_u\),由于我们可能用到子树补的信息,所以要做一遍换根 dp,或者为了方便以每个点为根都求一遍每个子树的答案,这显然不是复杂度瓶颈。

最后考虑求答案,因为最终点集一定是连通的,于是可以直接 dp 哪些极大的子树被删掉了。但第三个条件比较难处理,于是我们可以直接枚举 \(\sum siz_{u_i}\) 的值,dp 时维护 \(siz_{u_i}\) 之和,\(w_{u_i}\) 之和,根节点中是否出现了白点/黑点,转移为树上二维背包。算上外层的枚举总复杂度为 \(\mathcal{O}(n^5)\),常数很小可以通过。

Submission #71909654 - World Tour Finals 2022 Day1(Open Contest)

BSZX 2026.6.3 T3

给定 \(n,m\),求有多少个 \(n\times m\) 的 01 矩阵满足:

  • 每行的字符串从上往下满足字典序不降。
  • 每列的字符串从左往右满足字典序不降。

答案对给定模数 \(mod\) 取模,不保证 \(mod\) 为质数。\(n,m\le 70\)

\(res_{n,m}\) 表示每行每列字符串严格递增的结果,那么 \(res_{i,j}\)\(ans_{n,m}\) 的贡献为 \({n-1\choose i-1}{m-1\choose j-1}\),于是求出 \(res\) 即可。从上往下考虑每一行,发现我们会关心上一行的状态以及相邻两列是否已经比出大小,即列的等价类。

考虑直接把等价类缩为一个点,那么只需要维护一个 01 字符串。那么相当于第一行字符串为 \(0,1,01\),每行相当于对于每个字符都可以变为 \(0/1/01\),而因为还要求行字符串递增所以第一个发生变化的字符一定得是 \(0\) 变为 \(1/01\)。那么维护每行的二进制串,然后做插头 dp,即可做到 \(\mathcal{O}(2^nnm)\) 的复杂度。

考虑 AGC061E 的套路,即先只考虑第一列的字符什么时候发生变化,如果某次第一列的字符 \(0\) 变成了 \(1/01\),我们只关心 \(0\) 后面有多少个字符,每个字符都可以变为 \(0/1/01\) 中的任意一个。于是我们 dp 状态中应该记录行数,开头字符,初始字符(这些字符可以任意变为 \(0/1/01\)),以及最后得到了多少个字符。

\(f_{t,0/01,i,j}\) 表示 \(t\) 行,第一行开头为 \(0/01\),后面有 \(i\)\(0/1/01\),第 \(t\) 行共有 \(j\) 个字符的答案,\(g_{t,i,j}\) 表示 \(t\) 行,第一行有 \(i\)\(0/1/01\),第 \(t\) 行有 \(j\) 个字符的答案。考虑 \(g\) 的转移,枚举第一行开头是哪个字符,如果是 \(0/01\) 则转到 \(f\),否则因为第一列为 \(1\) 所以后面全是 \(1\),可以忽略这一列递归到子问题,即:

\[g_{t,i,j} = f_{t,0,i-1,j}+f_{t,1,i-1,j}+g_{t,i-1,j-1} \]

接下里考虑 \(f\) 的转移,首先考虑边界 \(t=1\),相当于 \(i\) 个字符任意变为 \(0/1/01\) 使得最终有 \(j\) 个字符,枚举最后一个字符变成了什么,即:

\[f_{1,o,i,j} = 2\times f_{1,o,i-1,j-1}+f_{1,o,i-1,j-2} \]

考虑 \(t>1\),如果第一列元素一直没有发生变化,有 \(f_{t,o,i,j}\gets g_{t,o,i,j-o-1}\)。否则枚举 \(p\) 表示在 \(p\)\(p+1\) 行时第一个 \(0\) 发生了变化,再枚举第 \(p\) 行除了开头共有 \(k\) 个元素,那么前 \(p\) 列的方案为 \(g_{p,i,k}\)。如果接下来 \(0\) 变为了 \(01\),那么应转到 \(f_{t-p,1,k+o,j}\),如果变为了 \(1\) 应转到 \(g_{t-p,k+o,j-1}\),即:

\[f_{t,o,i,j} = \sum_{p=1}^{t-1}\sum_{k=i}^j g_{p,i,k}\times (f_{t-p,1,k+o,j}+g_{t,i-1,j-1}) \]

总复杂度为 \(\mathcal{O}(n^5)\)。如果 \(mod\) 为质数的话,可以发现第一维是卷积形式,于是可以用拉差优化到 \(\mathcal{O}(n^4)\)

记录详情 - Bashu

Under the Epilogue - 题目 - QOJ.ac

给定 \(n\) 个区间 \([L_i,R_i]\),满足 \(L_i\le i\le R_i\)\(i\)\([L_i,R_i]\) 的每个点连边,从一个点 \(u\) 出发走若干条边回到 \(u\),求经过的点集 \(S\) 可能有多少种。

答案对 \(998244353\) 取模。\(n\le 50\)

一个点集 \(S\) 合法的条件为导出子图存在一条回路,也就是强连通。

考虑一个计数 SCC 的套路做法,去掉其中一个点后整个图会变为若干个 SCC。首先可以发现对任意一个点集 \(S\) 的导出子图缩点后,任意两个 SCC 要么无交要么一个完全包含另一个,否则可以合并这两个 SCC。

\(S\) 中编号的最下的点为 \(u\),将 \(S\setminus u\) 缩点,如果缩点后存在一个 SCC 完全包含另一个则一定不合法,所以一定是有若干个 SCC,每个 SCC 都是 \(S\) 一段区间中的所有点,并且每个 SCC 向前一个 SCC 有连边。此时加上 \(u\) 会缩为一整个 SCC 所以 \(u\) 向最右边的 SCC 有连边。

可以发现我们只关心一个 SCC 中编号最小值和最大值,以及 \(L_i\) 最小值和 \(R_i\) 最大值,设 \(f_{l,r,x,y}\) 为上述 SCC 的个数。枚举 SCC 编号最小点 \(l\),假设缩点后的 SCC 分别为 \((l_1,r_1,x_1,y_1),\ldots,(l_m,r_m,x_m,y_m)\),则要求 \(x_1\le l,R_l\ge l_m\),而相邻两个 SCC 要求后一个向前一个有连边且前一个不能向后连边,即 \(\forall 1\le i < m,x_{i+1}\le r_i,y_i < l_{i+1}\)

固定 \(l\) 后考虑从前往后加 SCC,设内层 dp \(g_{s,x,y}\) 表示最后一个 SCC 的最大值为 \(s\),所有点的 \(\min L_i=x,\max R_i=y\)。初始化为 \(g_{l,L_l,l} = 1\),转移时加入 \(f_{k,r,i,j}\),条件为 \(k\le R_l,y < k,s\ge i\)。这样总复杂度为 \(\mathcal{O}(n^8)\),常数很小可以通过,可以对 \(y\) 这一维做前缀和优化到 \(\mathcal{O}(n^7)\)

另一种做法是直接容斥,对于一个不是强连通的点集 \(S\),一直合并相邻两个 SCC 最终会得到若干个 SCC,同样做内层 dp 即可。

P14373 [JOISC 2018] 安全门 / Security Gate

定义一个括号序列是好的,当且仅当它可以通过将一个区间内所有括号取反后变成合法括号序列(取反为左括号变右括号、右括号变左括号,区间可以为空),给定一个含 \((,),x\) 的字符串 \(S\),求将每个 \(x\) 替换为 \((\)\()\) 后能得到多少好的括号序列。

答案对 \(10^9+7\) 取模。\(|S|\le 300\)

\((,)\) 分别看为 \(1,-1\),设前缀和为 \(s_i\),那么翻转区间 \([l+1,r]\) 后得到的括号序列合法的条件为 \(\forall 0\le i\le l,s_i\ge 0,\forall l\le i\le r,s_i\le 2s_l,\forall r\le i\le n,s_i\ge s_n\)\(s_r-s_l=\frac{s_n}2\)。对于第一个和第三个条件相当于限制了 \(l\) 只能在一段前缀里取,\(r\) 只能在一段后缀里取。这个前缀为第一个 \(s_i<0\) 前和最后一个 \(s_i<s_n\) 后,可以发现这个前缀和后缀要么无交要么其中一个是整个序列,于是考虑分讨。

如果两个限制都是整个序列,即 \(\forall 0\le i\le n,s_i\ge 0,s_i\ge s_n\),则 \(s_n=0\),也就是说这本来就是一个合法括号序列,计数是简单的。

如果只有后缀限制是整个序列,即 \(\forall 0\le i\le n,s_i\ge s_n\),则 \(s_n<0\),设 \(p\) 表示第一个 \(s_i<0\) 的前一位 \(i-1\),那么要求 \(l\in [0,p]\)。因为要求 \([l,r]\) 内的 \(i\) 满足 \(s_i\le 2s_l\),所以 \(s_l\) 越大越好这样限制更松且 \(s_l+\frac{s_n}2\) 更靠前,所以 \(l\) 一定是取 \([0,p]\)\(s\) 的最大值 \(s_{mx}\) 所在 \(mx\),那么条件为找到 \(mx\) 后最近的 \(s_{mx}+\frac{s_n}2\),这之间的数都 \(\le 2s_{mx}\)

考虑计数,首先处理出 \(f_{i,j,k}\) 表示前 \(i\) 个字符,和为 \(j\)\(s\) 最大值为 \(k\) 的方案数,这部分复杂度 \(\mathcal{O}(n^3)\)。接下来可以枚举 \(s_{mx},s_n\),枚举 \(p\) 后再 dp,dp 时需要记录当前的 \(s\) 以及是否遇到了 \(s_{mx}+\frac{s_n}2\),这样复杂度是 \(\mathcal{O}(n^4)\) 的。考虑分别 dp 从前往后和从后往前的答案然后拼起来。从后往前 dp 的话就不能再枚举 \(s_{mx}\) 了,可以把第二个条件改为 \(\forall l\le i\le r,s_i-s_n\le 2(s_r-s_n)\)

于是我们枚举 \(t=s_r-s_n\),这样子从后往前 dp 时只需要记录 \(s_i-s_n\) 的值,即后缀和。遇到一个后缀和 \(=t\) 的位置可以选择是否成为 \(r\),成为 \(r\) 后要求之后的后缀和都 \(\le 2t\),并且因为条件是找最近的 \(s_r\) 所以之后后缀和都不能为 \(t\)。设 \(g_{i,j,0/1}\) 表示从后往前考虑到了 \(i+1\),后缀和为 \(j\),是否确定了 \(r\),注意还要求 \(j\ge 0\)

统计答案时枚举 \(p,s_{mx}\),算出 \(s_n = 2(s_{mx}-t),s_r = s_l+\frac{s_n}2\),如果 \(0\le s_r\le s_{mx}\),那么最靠近的 \(r\)\(p\) 之前,后面不应该确定 \(r\),还有一个要求第 \(p+1\) 个数一定是 \(-1\),则贡献为 \(f_{p,0,s_{mx}}\times g_{p+1,-1-s_n,[\neg(0\le s_r\le s_{mx})]}\)。复杂度为 \(\mathcal{O}(n^3)\)

如果只有前缀限制是整个序列,做法和上面一样,可以将整个序列翻转并将括号取反再做一遍。

如果前后缀限制都不是整个序列,那么两个限制一定不交,设前缀限制为 \([0,p]\),后缀限制为 \([q,n]\),分别找出前缀、后缀中的最大值 \(s_x,s_y\),因为第二个条件所以在 \(r\) 存在的条件下 \(s_l\) 越大越好。如果 \(s_x+\frac{s_n}2\le s_y\)\(r\) 一定存在所以 \(l\)\(x\) 最优,此时的判定条件和上面一样,否则 \(s_y-\frac{s_n}2\le s_x\),那么 \(r\)\(y\) 最优,判定条件同理。

考虑计数,如果 \(s_x+\frac{s_n}2\le s_y\) 那么做法和上面大致相同,还是枚举 \(t=s_r-s_n\),不同的是可以有 \(j<0\),并且要将 \(0/1\) 这一维改为 \(0/1/2\)\(2\) 表示确定了 \(r\) 并且存在后缀和 \(<0\) 的位置。在 \(2\) 状态下还是要求 \(j\le 2t\) 但是可以有 \(j=t\),统计答案时贡献为 \(f_{p,0,s_{mx}}\times g_{p+1,-1-s_n,2}\),可以和上面的 dp 一起做。

如果 \(s_y-\frac{s_n}2\le s_x\),将整个序列翻转并将括号取反再做一遍即可。最后考虑如果 \(s_x+\frac{s_n}2=s_y\) 还需要去重,dp 思路和上面相同,但在 \(0,1\) 状态下还增加了 \(j\le t\) 的限制。总复杂度 \(\mathcal{O}(n^3)\)

记录详情 - 洛谷 | 计算机科学教育新生态

posted @ 2026-06-02 21:49  max0810  阅读(54)  评论(0)    收藏  举报