do_while_true

一言(ヒトコト)

Codeforces 杂题 II [2700, 3500]

1408G | 2700 | D

边权从小到大插入,在某一时刻形成了一个团,那么这个连通块就可以划分成一组。

没想到的:合法的组和组之间一定是包含或者相离的关系,所以在 Kruskul 重构树上树形 dp \(\mathcal{O}(n^2)\) 树形 dp 即可。

1383E | 2800 | D

确实是根据贪心匹配来做 dp,但是没想明白怎么贪。

对操作序列进行计数是难以计重的,所以考虑对最终形成的串进行计数。

首先头尾连续的 0 可以任意选几个都行,头有 \(x\) 个尾有 \(y\) 个,那么最后答案乘上 \((x+1)(y+1)\) 即可,然后就可以不管它们了。

然后考虑一次操作是可以任意删去一个 0,或者在两个相邻的 1 中删除一个 1.那么考虑将相邻的 \(1\) 中间 \(0\) 的个数拉下来组成一个序列,那么操作就是将一个数 \(-1\) 或者删除一个 \(0\)

如果 \(s\) 对应的序列为 \(a\)\(t\) 对应的序列为 \(b\),那么 \(t\) 能被 \(s\) 生成当且仅当 \(a\) 中能够找到一个和 \(b\) 等长的子序列,满足对应位置都 \(\geq\) \(b\) 对应的位置。

考虑怎样给串划分等价类使得能够 dp.套路是考虑如何 check 一个 \(b\) 合法,那么就是每次贪心找到后面第一个 \(\geq\) 当前数的 \(a\),然后进行一个匹配。

所以设 \(f_i\) 表示能够从 \(a_{[1,i]}\) 生成,并且更小的前缀都无法生成的 \(b\) 的个数(其实就是匹配到了 \(a_i\)\(b\) 的个数)。考虑如果 \(b\)\(a_i\) 匹配的这里填了一个 \(x\),上一次填的位置是 \(j\),那么 \(j\) 合法当且仅当 \((i,j)\) 中的所有 \(k\) 都满足 \(a_k<x\).那么就可以找到满足 \(a_p\geq x\) 的最大的 \(p\),则这个位置填 \(x\) 的方案数是 \(\sum_{j=p}^{i-1}f_p\)

由于序列中所有数总和是 \(\mathcal{O}(n)\) 的,所以直接暴力枚举可行的 \(x\),用单调栈来找 \(p\),用前缀和来转移,即可做到时间复杂度 \(\mathcal{O}(n)\)

1366G | 2700 | C

瞄了眼题解。

\(f_{i,j}\)\(s[:i]\) 生成 \(t[:j]\) 的最小步数。令 \(p_i\) 为与 \(i\) 括号匹配的位置。

  • 如果 \(s_{i+1}\)\(t_{j+1}\) 相同,则 \(f_{i+1,j+1}\gets f_{i,j}\)
  • \(f_{i+1,j}\gets f_{i,j}+1\),忽略这个字符;
  • 中间有一段括号匹配了,\(f_{i+1,p[i+1]}\gets f_{i,j},p_{i+1}>i+1\)

Code

1342F | 3000 | A

只会 \(\mathcal{O}(3^nn^2)\),打开题解一看怎么还真是这个玩意/jy

首先集合之间形成一个 sum 和 pos 的二维偏序,那么思路就是对一维扫描线,然后另一维搞个什么东西。具体到这个题就是按照集合 sum 枚举集合,然后用状压 dp 记录 pos.

按照 sum 扫描线,令 \(f_{S,i}\) 当前选中的数状态集合为 \(S\),最后一个位置在 \(i\),假如当前枚举的集合是 \(T\),那么 \(f_{S,i}\) 就能转移到 \(f'_{S\cup T,j}\),这里要求 \(S\cap T=\emptyset,i\in S,j\in T\)

要求 \(S\cap T=\emptyset\) 来转移实际上就是枚举子集复杂度,再算上第二维 \(i\) 以及枚举 \(j\),复杂度是 \(\mathcal{O}(3^nn^2)\),常数小可以过。

代码实现上有一点细节(也有可能是我写麻烦了),记录前驱的时候要记录 \(pre_{S,i,j}\)\(f_{S,i}=j\) 时候的前驱,因为在转移过程中 \(f\) 可能会变。

其实这个 dp 的本质是定义域和值域交换的套路。

Code 74min

1326F2 | 3200 | D

没想到容斥,瞄了一眼题解。

上一发容斥,考虑钦定一些位置的 \(s=1\),最后子集反演(高维后缀差分)一下就得到答案了。

然后考虑这个方案数只和极长连续 \(1\) 段构成的多重集有关,也就是连续的一段具有认识关系的人。对于所有的 “连续的一段具有认识关系的人数” 构成的多重集算一下方案数就行。需要满足总和恰好为 \(n\),搜出来 \(n=18\) 时多重集个数只有 385 个。

怎么算方案数?令 \(h_{S}\) 表示用一条路径将点集 \(S\) 内的点连起来的方案数,这个可以多记一个路径末尾的点然后 \(\mathcal{O}(2^nn^2)\) dp 出。然后考虑多重集 \(\{a_1,a_2,\cdots,a_k\}\) 的方案数实际上就是:

\[\sum_{S_1\cup S_2\cup\cdots S_k=U,|S_i=a_i|}\prod g_{S_i} \]

如果没有 \(|S_i|=a_i\) 的限制那么就可以直接 FMT 了,所以强行添加一个维度令其满足 \(|S_i|=a_i\)。所以令 \(g_{i,S}\) 表示一条长度为 \(i\) 的路径能够连起来点集 \(S\) 的方案数(实际上就是 \(i=|S|\)\(g_{i,S}=h_S\),否则为 \(0\))。

这样方案数就变成了:

\[\sum_{S_1\cup S_2\cup\cdots S_k=U}\prod g_{a_i,S_i} \]

提前预处理出 \(g\) 作高维前缀和(FMT-or)的点值,在 dfs 的过程中乘点值即可做到 \(\mathcal{O}(2^n(P(n)+n^2))\) 时间复杂度。其中 \(P(n)\)\(n\) 的划分数。

1299D | 3000 | D

???

先考虑如何判定是否存在 xor 为 \(0\) 的环,这个就用最大XOR和路径那个题的做法,在 dfs 树上将所有返祖边与其经过的树边形成的环的 XOR 扔进线性基里,只有满秩才满足不存在 xor 为 \(0\) 的环。

由于边权很小,线性基的个数很少只有 \(374\) 个,所以考虑将线性基编号作为状态来 dp.

就依次考虑删去 \(1\) 之后剩余的连通块,如果仅有一个点和 \(1\) 有边相连,就考虑删和不删两种情况;如果有两个点和 \(1\) 有边相连,就考虑都不删(就把这个连通块的线性基和这个包含 \(1\) 的三元环插入)或者都删(不对线性基进行改变),或者只删一个(只把这个连通块的线性基插入)三种情况。

如果提前预处理出所有线性基和另一个线性基合并的结果,时间复杂度可以做到大略 \(\mathcal{O}(cn)\)(不考虑预处理线性基复杂度,其中 \(c\) 是线性基个数).

1292F | 3500 | D

如果 \(a_i\operatorname{|}a_j\)\(i\to j\) 连一条边。首先不同的弱连通块可以分别求出答案再用组合数乘起来。

对于每个弱连通块,将入度为 \(0\) 的点组成集合称为 \(S\)(一定不会被删掉的),其余点为 \(T\)(可能被删掉的),可以断言最多删掉的个数就是 \(|T|-1\).具体构造可以倒过来看,初始 \(T\) 中只有最后留下来的那个点,每次选择一个 \(u\in S,v\in T,w\notin T\),并将 \(z\) 加入 \(T\).由于整除关系具有传递性,并且它们处于同一个弱连通块,那么如果还有数没有被加入的话容易证明一定存在一个数可以加入 \(T\)

可以根据这个设计 dp 来计数,加入 \(w\) 的时候必须存在 \(v\in T\) 使得有共同因子 \(u\in S\).那么将 \(T\) 的因子计入状态,\(f_{A}\) 表示 \(T\)\(\in S\) 的因子组成的集合是 \(A\) 的方案数。

如果一个 \(w\)\(\in S\) 的因子集合 \(A_w\)\(A\) 有交并且还没有加入过,则可以加入。

  • 如果加入后可以让 \(A\) 变大那么一定可以加入;

  • 否则有 \(A_w\in A\),这个时候发现没办法判断一个点是否已经被加入,但是知道 \(A_w\in A\),这启发我们可以提前记录 \(cnt_A\) 表示满足 \(A_w\in A\)\(w\) 的个数,同时在 \(f\) 记录一共选了 \(c\) 个数,那么即有转移 \(f_{A,c}\cdot (cnt_A-c)\to f_{A,c}\)

所以前面那部分的转移是 \(f_{A,c}\to f_{A\cup A_w,c+1}\)

暴力实现复杂度是 \(\mathcal{O}(2^{|S|}\cdot |T|^2)\).可以注意到 \(|S|\) 实际上不是很大。首先 \(>\frac{N}{2}\) 的一定没有用,它没有任何倍数也就没有出边,又没有入边的话它就是一个孤立点。考虑 \(\leq \frac{N}{2}\) 的数,又要满足两两不整除,一个毛估估的方法是记 \(f(x)\)\(x\) 不断除以 \(2\) 得到的奇数,如果 \(f(x)=f(y)\) 那么一定可以推出 \(x,y\) 具有整除关系。所以这些数两两不整除的一个必要条件是 \(f(a_i)\) 互不相同,既然 \(x\leq \frac{N}{2}\)\(f(x)\) 只有 \(\frac{N}{4}\) 种不同的取值,故 \(|S|\leq \frac{N}{4}\)

1290F | 3500 | A

一年半前我在省队集训的赛场上,折戟沉沙。一年半后,我从倒下的地方再一次倒下。

我依然没成功。我还是以前的那个我。

然而做法还是靠回忆课上讲了啥才想起来数位 dp,然而难点就是想到数位 dp。

首先需要把所有向量极角排序,这样就能发现凸包的实际形态仅和各个向量的个数有关。然后考虑限制相当于横纵坐标都要满足正数的和等于负数绝对值的和,并且要 \(\leq m\)

然后考虑数位 dp,\(m\) 以及各个向量的个数这六个数同时填。

所以就记 \(f_{i,xpos,xneg,ypos,yneg,0/1,0/1}\) 表示从低到高填到第 \(i\) 位,\(a,b,c,d\) 分别表示横/纵坐标的正/负分别进位多少,以及横/纵坐标的正数总和的最低 \((i-1)\) 位是不是 \(\leq m\) 最低 \((i-1)\) 位。

转移需要 \(\mathcal{O}(2^n)\) 枚举都填啥,就能算出能不能转移,转移到哪里。然后状态数 \(i\) 个数为 \(31\)\(a,b,c,d\) 个数都为 \(20\),这样算量是 \(31\times 20^4\times 2^5\approx 1.5\times 10^8\) 可以通过。

1286F | 3100 | D

分析一下有啥性质。二操作看作边,那么连起来的一定是若干棵树,因为一个连通块里如果操作数 \(\geq\) 点数,还不如直接用一操作一个一个清零。所以二操作看作边的话一定连出来一个森林,并且每棵树内部都只用二操作全消成零。

然后再找一下性质,写一写就发现实际上一个集合能成为一个合法的树,它的奇数层之和与偶数层之和的差要 \(\leq\) 总点数 \(-1\),且与总点数 \(-1\) 奇偶性相同。先求出来每个集合是否合法然后跑个子集卷积就行。

后面就不会了。看了眼题解,实际上也能分析出来。这是一个背包问题,值域很大那就只能考虑复杂度是指数级别的算法,对每个集合 \(S\) 跑个 meet in the middle,搜出来左半部分和右半部分的差(可以搜的过程中归并,这样就少个 log),复杂度是 \(T(k)=T(k-1)+2^{k}=\mathcal{O}(2^{|S|/2})\),然后双指针扫。那么这部分总的复杂度就是 \(\mathcal{O}(\sum_S 2^{|S|/2})\),考虑每个数选或者不选的贡献,复杂度大概就是 \(\mathcal{O}((1+\sqrt 2)^n)\)

最后算答案,\(\mathcal{O}(3^n)\) 常数很小,可以剪枝优化让每个集合只被最小的集合更新)也能跑过去。最后这个剪枝写出来是这个东西(来自洛谷题解)。

for(int s=1;s<=all;s++) if(!f[s]&&check(s)){//!f[s] can cut tons of situations.
	int r=all^s; f[s]=1;
	for(int t=r;t;--t&=r) f[s|t]=max(f[s|t],f[t]+1);
}

或者子集卷积,考虑暴力子集卷积每次是 \(\mathcal{O}(n^2\cdot 2^n)\) 卷几次,看卷几次把全集给卷出来。倍增优化一下就能做到 \(\mathcal{O}(\log n\cdot n^2\cdot 2^n)\)

1279F | 2800 | D

模板题一点思路也没有?之前学的东西都忘到哪里去了??/fn

大小写分开做,然后考虑答案关于操作个数 \(k\) 的函数 \(f(k)\) 是一个下凸函数,所以上个 wqs 二分就做完了。

1276D | 2900 | B

删除看起来结构非常混沌,需要转化一下问题,一个点被哪条边删掉了就让点和边匹配起来。一组匹配合法当且仅当:

  • 不存在一条非匹配边和一个非匹配点相邻;
  • 不存在一条非匹配边 \(x\),其两端点匹配的边编号要比 \(x\) 小。

然后发现由于第二条限制,所以以子树 dp 的话,需要记录子树这个根所匹配的边,和根到父亲匹配的边的大小关系。

就记 \(f_{u,0/1/2/3}\) 表示 \(u\) 子树内匹配,若 \(u\) 和父亲的边编号为 \(k\),那么 \(u\) 没匹配 / 和父亲匹配 / 和 \(<k\) 的边匹配 / 和 \(>k\) 的边匹配的方案数。然后大分讨来 dp 就行。

不太想写代码,先咕咕咕,有时间要来写这题。

1225G | 3100

有点高。

如果直接考虑每次删俩数再添进去一个是比较困难的,因为不好记录添进去之后的数。所以要考虑每个数最后的贡献,一定是 \(\frac{a_i}{k^{p_i}}\) 的形式。那就考虑什么样的 \(p_i\) 是合法的,首先一个必要条件是 \(\sum \frac{a_i}{k^{p_i}}=1\),然后发现其也是充分的,只需要每次找 \(p_i\) 最大的两个数合并起来就行,因为 \(\sum \frac{a_i}{k^{p_i}}=1\) 是个整数,那么 \(p_i\) 最大的那些 \(a_i\) 的和一定是 \(k\) 的倍数,否则就凑不出整数了。

那现在就是考虑是否有个序列 \(p_i\) 满足 \(\sum\frac{a_i}{k^{p_i}}=1\).套路就是 dp 令 \(f_{S,i}\) 为集合 \(S\) 是否能够合并出 \(i\),观察出如何转移不太容易,但似乎是这个题

考虑充分性的那个构造,每次选出一些数 \(a_i\),删除它们之后将它们总和除以 \(k\) 加入到集合中,再让它们的 \(p_i\)\(1\)(也就是构造过程中,让 \(p_i=\max p=m\) 的合并到一起,然后 \(m\gets m-1\)).再不断重复这个过程。

这些过程可以看成两个操作:加入一个数 / 总和除以 \(k\).所以可以列出 dp 转移:\(f_{S,x}=f_{S,x\cdot k}\cup (\cup_{i\in S}f_{S\backslash\{x\},x-a_i})\)

bitset 优化,输出方案直接找到一个可行路径,然后根据构造方法模拟即可。

时间复杂度:\(\mathcal{O}((2^n\cdot n\sum a)/\omega+2^n\sum a)\) .

1197F | 2700

¿ 什么堆砌套路题。

把每个纸条取 SG 值为 \(0,1,2,3\) 的方案数算出来,然后 dp 一下算 SG xor 为 \(0\) 的方案数。

对每个纸条算方案数,就直接列矩阵,类似于 dp 套 dp,内层套的是递推 SG 函数的 dp 状态一共有 \(k=4^3=64\).把当前这一位,上一位,上上位,这三位的 SG 值记下来,然后颜色状态和状态之间的转移系数就能列出来。对于已经染色的特殊转移,中间的矩阵快速幂。

这样复杂度就是 \(\mathcal{O}(mk^3\log a+n)\).每次矩阵快速幂的转移矩阵都不会变,就用美食家那个套路,预处理转移矩阵的 \(2^k\) 次幂,每次 \(\mathcal{O}(k^2)\) 乘上去就行。这样复杂度就是 \(\mathcal{O}(mk^2\log a+k^3\log a+n)\) 的了。

1194G | 2700

考虑将约分完相同的 \(x',y'\) 分为一组.对于 \(x'=y’\) 这种特殊情况单独计算。所以一组里 \(x',y'\) 最多有 \(4\) 个(1/2, 2/4, 3/6, 4/8),然后只要这些 \(x',y'\) 有一对都出现过了就合法了。然后考虑 \(x=zx',y=zy'\)\(z\) 进行一个数位 dp.

直接 dp 就是 \(f(i,\Delta x,\Delta y,maskx,masky,0/1/2)\),因为要保证差为 \(0\) 而且有个乘法,所以要从低往高模拟竖式乘法,\(\Delta x\)\(x=zx'\) 往上的进位,\(mask x,masky\) 表示当前枚举到的数码的出现情况,最后一个是记录 \(zx'\)\(n\) 的低 \(i\) 位的大小关系。这里的 \(x',y'\) 是组里面最小的,就是那个既约分数。

一个小优化就是注意到四进制里面有一个 \(3\) 就合法了,所以只需要记三进制就行了。

1152F2 | 3000

扫值域,然后考虑逐步 \(a\) 通过插数给插出来。然后发现如果选,那么可以插入的位置就是 \([i-m+1,i-1]\) 里面选了的数的个数再 \(+1\)(它们后面都能插入 \(i\),还有直接插入到最开头的一种情况)

那么就有 dp \(f_{i,j,S}\) 表示考虑到了 \(i\),插入了 \(j\) 个数,\([i-m+1,i-1]\) 的选择情况是 \(S\). 

列出 dp 之后把后两维看成一个列向量,矩阵快速幂即可。

其实,还挺好写的。

1146H | 2900

对五个点的凸包进行计数。关键在于怎么描述一个凸包,斜率单调,或者说边可以按极角排序顺序排出来。

所以就把所有边极角排序一下,然后 \(f_{i,j}\) 表示走 \(i\) 条边走到 \(j\) 的方案数,按极角排序枚举一条边 \((u,v)\),并更新 \(f_{i+1,u}\gets f_{i+1,u}+f_{i,v}\)

不会计重,因为每个凸包只有唯一的一个极角排序遍历边的方案。

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

1119F | 3400

APIO 2021 封闭道路 /zk

首先考虑固定 \(x\) 怎么做,\(f_u\) 表示强制 \(u\) 与父亲的边不选,\(u\) 的子树合法的最小代价。\(g_u\) 表示强制选上 \(u\) 与父亲的边,\(u\) 的子树合法的最小代价。

假如说限制了最多和儿子连 \(d\) 个,那么就是选 \(\leq d\)\(g_v\),剩下的选 \(f_v\).也就是强制都选 \(f_v\) 之后,选 \(g_v-f_v\) 最小的 \(d\) 个负的。这里 \(v\)\(u\) 的儿子。

然后考虑如果 \(deg_u,deg_v\leq x\),那么 \((u,v)\) 这条边一定不需要断,就可以不考虑了。如果只看 \(deg_u>x\)\(u\)(称之为合法点),那么总的需要考虑的合法点的个数就是 \(2n-1\)

但是合法点的总度数会寄掉,在每个点处维护一个堆存放着 \(g_v-f_v\) 最小的 \(d\) 个负的,并且这里 \(v\) 是不合法点。对于合法点形成的所有连通块,叶子直接看这个堆里面记录的元素总和就知道它的 \(f,g\),然后再传给父亲。如此 dfs dp 即可,dp 完了还需要还原这些堆。

然后就考虑点从合法变成不合法,就直接传给父亲的堆就行了。

这样时间复杂度就是 \(\mathcal{O}(n\log n)\)

1097G | 3000 | C

\(k\) 很小,遇上 \((f(X))^k\) 第一反应应该是用第二类斯特林数把这个展开。

这样答案就相当于 \(\sum_i {\begin{Bmatrix}k\\i\end{Bmatrix}}i!\sum_X \binom{f(X)}{i}\)

那么问题就在于对于每个 \(i\) 计算出 \(sum_i=\sum_X \binom{f(X)}{i}\),考虑它的组合意义是 \(X\) 形成的虚树上选 \(i\) 条边的方案数,那么尝试依靠这个组合意义来设计 dp。

\(f_{u,i}\) 为考虑 \(u\) 这个子树,一条边被选择当且仅当其下面的子树中有点在 \(X\) 中,选 \(i\) 条边的方案数。

\(g_{u,i}\)\(f_{u,i}\) 类似,但是多考虑了 \(u\) 到父亲的那条边。

初始状态就是对于 \(u\) 是个虚树中的叶子的话 \(f_{u,0}=2\)(这个点是否在 \(X\) 中都能选 \(0\) 条边)。

考虑 \(f\) 的转移,是一个树形背包,每次合并上来一个子树 \(v\) 的时候 \(f_{u,i}\times g_{v,j}\to f'_{u,i+j}\)

考虑 \(g\) 的转移,\(g_{u,i}=f_{u,i-1}+f_{u,i}\),对应着这条边选或者不选。\(g_{u,1}\) 需要多减去 \(1\), 也就是多算了 \(u\) 子树内一个点也不在 \(X\) 内的情况。

dp 出这个考虑如何计算 \(sum_i\),先加上 \(f_{u,i}\),然后考虑有哪些不合法的情况被计算了,就是 \(X\) 中所有的点都在 \(u\) 的一个子树 \(v\) 中的情况,那么再减去 \(g_{v,i}\) 即可。

树形背包复杂度为 \(\mathcal{O}(nk)\),求斯特林数复杂度为 \(\mathcal{O}(k^2)\),总复杂度就是 \(\mathcal{O}(nk+k^2)\)

/jy 这里的树形背包竟然是有数学意义的。范德蒙德卷积/xia

Code 22 min

1085G | 2900 | D

数排名,就是数比给定的矩阵 \(A\) 更小的矩阵 \(B\) 的个数。常见的套路是枚举 \(B\) 从哪一位开始比 \(A\) 小,这样方案数就是这一行后面空位的方案数,乘上下面错排数 \(D_n\) 的若干次方。重要的是怎么数后面空位的方案数。

如果是第一排就是一个阶乘。但是后面的排,上一排可能会带来一些限制。

当然第一个位置需要比 \(A\) 所在位置小,假如先枚举了这个位置是几。那么现在限制就变为了:

\(i\) 个空位待填,有 \(j\) 个位置是不能冲突需要错位的,假设方案数其为 \(f_{i,j}\)

容易发现只有这个位置只有填当前列以前上一行出现过了的,和没出现过的两种方式,对应的不同的 \(f\).这个方案数可以用树状数组快速求得,考虑如何计算出 \(f\)

首先考虑一些特殊情况,\(f_{i,0}=i!,f_{i,i}=D_i\).然后考虑 \(f_{i,j}\) 能不能递推算出。

回顾一下错排数的递推式是如何求得的?单独拎出最后一个位置 \(n\) 及其代表的数 \(n\),先考虑长度 \((n-1)\) 的错排,讨论将位置 \(n\) 以及数 \(n\) 插入时不同方式的方案数之和。

尝试对这个方法进行抽象化。对一个满足一些条件的组合对象进行计数时,想要通过规模更小的问题的答案来获得当前问题答案,考虑去除组合对象中的若干元素,然后考虑将这些元素在规模更小的组合对象中插入后,方案数之和是多少。

方法一:考虑将一个限制不能冲突的位置及其代表的数加入,首先考虑这个数放入了前面哪个位置。

第一种情况是放入一个有限制的位置,有 \((j-1)\) 种放法。放入之后剩下一个单独的随便放的数,和一个单独的随便放的位置。把这两个看作同一个编号,那么就化为了 \((i-1)\) 个数及位置,有 \((j-2)\) 个限制的情况。

第二种情况是放入一个无限制的位置,有 \((i-j)\) 种放法。放入之后同理可以将单独的数和单独的位置看作同一个编号,就化为了 \((i-1)\) 个数及位置,有 \((j-1)\) 个限制的情况。

所以递推式为 \(f_{i,j}=(j-1)f_{i-1,j-2}+(i-j)f_{i-1,j-1}\)

方法二:考虑将一个无限制的位置及数放入。

第一种情况是放入一个有限制的位置,可推得这种情况的方案数 \(jf_{i-1,j-1}\)

第二种情况是放入一个无限制的位置,这种情况的方案数 \((i-j-1)f_{i-1,j}\)

递推式为 \(f_{i,j}=jf_{i-1,j-1}+(i-j-1)f_{i-1,j}\)

值得注意的是 \(i=j\) 时不存在无限制的数,所以需要初始化的是 \(f_{i,i}\)

方法三:考虑将一个有限制的位置及数放入,假装没有限制先任意选 \(f_{i,j-1}\),再容斥掉不合法的情况(强制那个有限制的冲突) \(-f_{i-1,j-1}\)

递推式为 \(f_{i,j}=f_{i,j-1}-f_{i-1,j-1}\)

1082F | 2800 | D

感觉一下做法,把 Trie 建出来,尝试在上面 dp.

首先根据套路可以设一个 dp \(f_{u,i}\) 表示为考虑点 \(u\) 的子树,选了 \(i\) 个快捷键,合并需要做个背包。问题在于需要记录什么状态,使得能够算出当前节点选/不选快捷键的代价是多少。

尝试将每个数字串费用摊到每个节点上面,也就是假设一个节点上面离它最近的快捷键节点距离为 \(d\),那么不设快捷键的代价就是 \(d\times size_u\)

这样记录 dp 状态为 \(f_{u,d,i}\) 考虑 \(u\) 的子树,选了 \(i\) 个快捷键,距离 \(u\) 最近的祖先快捷键距离为 \(d\),dp 的合并就是在 \(i\) 那一维是个背包。

时间复杂度就是 \(\mathcal{O}(n^3)\)

1067E | 2800 | C

看上去是个结论题,于是先瞄了一眼结论。

结论是,森林的邻接矩阵的秩是最大匹配的点数(也就是最大匹配 \(\times 2\))。

首先邻接矩阵是个对称矩阵,秩是非零子式的最高阶数。

然后掏出了一个结论,这个对称矩阵,只看主子式即可。

怎么证的?优质解答我不知道。

qwaszx:证明就是你可以自行验证合同变换下这个东西不变 然后化成正交相似标准型

qwaszx:不过u1s1 你想明白我说的话可能得花两周学学线代

我选择摆烂。

然后考虑一个主子式,在算的实际上就是一个点导出子图(仍然为一个森林)的行列式。

把这个行列式的式子写出来,考虑什么样的森林行列式不为 \(0\)\(\det (E)=\sum \mathrm{sgn}(\sigma)\prod E_{i,\sigma_i}\)

考虑右边的 \(\prod E_{i,\sigma_i}\),只有 \((i,\sigma_i)\) 这样的边都存在值才为 \(1\),又有 \(E_{i,i}=0\),树中并没有无向的环。所以其不为 \(0\) 只能是若干个二元环,对应到树上就是一组完美匹配。

然后考虑树求最大匹配的算法,从底向上贪心能匹配就匹配,因为考虑一个叶子不和其父亲匹配一定不会更优。根据这个算法不难得出完美匹配是唯一的。

所以 \(\det(E)\) 非零,即 \(E\) 满秩当且仅当这个森林存在完美匹配。而最大的点导出子图满足其存在完美匹配,就是原森林的最大匹配,则结论得证。

现在问题变成了,森林的每条边可以出现和不出现,求所有方案中最大匹配的总和是什么。

可以 dp,上面已经提到了一个树求最大匹配的算法,依据这个列一个 dp 即可。

\(f_{u,0/1}\) 表示在贪心匹配策略下,仅考虑 \(u\) 子树内的方案,所有方案中 \(u\) 没被选/被选 的情况下,最大匹配数的和是多少。

为了让 \(f\) 能够转移,还要 dp 一个 \(g_{u,0/1}\) 表示在贪心匹配策略下,仅考虑 \(u\) 子树内的方案,所有方案中 \(u\) 没被选/被选 的情况下,方案数一共有多少。

具体如何转移直接讨论即可。

也可以直接计数,将问题转化成对于每个 \(u\) 计算仅考虑 \(u\) 子树内的方案,\(u\) 和儿子匹配的方案数是多少。这个直接递推也是容易算的。

这样问题就解决了。两个做法的时间复杂度是 \(\mathcal{O}(n)\)

Code

1063F | 3300 | D

为了满足正常人类的思考方式,先 reverse 一下。

不难看出选出的字符串长度为 \(1,2,\cdots,k\) 一定不劣,仅考虑这种形式的。

然后考虑一手 dp,设 \(f_{i}\) 表示最后一个子串是 \(i\) 为结尾,最长长度是多少。

这样转移就是 \(f_i\gets f_{j}+1,iff\ s[j-f_j+1,j]\text { is } s[i-f_j,i] \text{'s substring}\)

然后拿个哈希表转移一下,复杂度 \(\mathcal{O}(n\sqrt n)\)

\(\mathcal{O}(n\log n)\) 做法的思路没见过啊??

掏出了一个结论,\(f_{i+1}\leq f_i+1\),因为不成立的话 \(f_i\) 一定可以调整得更大。

那么问题就可以从求解变成判定。具体就是初始时 \(i=k=1\),判定 \(f_i\) 是否 \(\geq k\),如果成功则 \(i,k\)\(+1\),否则只有 \(k\gets k-1\),然后继续判定。总共判定 \(\mathcal{O}(n)\) 次。

那么现在问题就是判定 \([i-k+1,i]\) 能否选出,一种情况是上一个字符串 \(t\)\(s[i-k+1,i-1]\),另一种情况是 \(s[i-k+2,i]\),注意到每次 \(i,k\) 同时 \(+1\) 相当于右端点向右一步;\(k\gets k-1\) 的时候相当于左端点向右一步。那么可以直接动态地维护这两个串在 SAM 上的位置,右端点向右一步就直接走自动机上的边,左端点向右一步就看看 \(len\) 是否还在当前节点的 \(len\) 范围中,如果不在就跳一步 parent 树上的父亲。

定位出这个节点之后,考虑需要判断是否存在上一个选出的串的右端点 \(j\),其合法要满足 \(f_j\geq k-1\)\(j\leq i-k\)\(t\)\(s[j-f_j+1,j]\) 的后缀.

注意到每次 \(i,k\) 变化时是同时 \(+1\) 或者 \(k\gets k-1\),那么 \(i-k\) 是不降的。于是扫 \(i,k\) 的时候再每次把 \(f_{i-k}\) 给 SAM 上 \(s[i-k-f_{i-k}+1,i-k]\) 所代表的节点单点 \(\text{chkmax}\),查询的时候就查 parent 子树 \(\max\),就可以了。这样总的时间复杂度是 \(\mathcal{O}(n\log n)\)

仔细想一想,由于子树中的等价类 \(len\) 一定大于当前节点的 \(len\),所以子树中一旦出现了一个单点 \(\text{chkmax}\),那么自己肯定是合法的,所以只需要记录出每个点子树中(不包含自己)是否已经出现过点,记作 \(ok\),并且记录每个点处的 \(f\)\(\max\),判断合法时直接判 \(f\geq k\) 或者 \(ok\) 已经是否已经被标记过即可。扫 \(i,k\) 的时候加入 \(f_{i-k}\) 时,暴力不断在 parent 树上跳 father 更新 \(ok\) 即可,如果之前已经跳到过了就不再跳了(一个点被标记那么其祖先已均被标记)。这样均摊下来总的时间复杂度是 \(\mathcal{O}(n)\)

1060F | 2900 | D

讲得很好。

枚举一下根,考虑算这个根最终存活的概率是多少。

想想怎么设计树形 dp?考虑一条边 \((u,v)\)\(u\)\(v\) 的父亲。在删掉这条边之前,\(v\) 子树内的边怎么删都不会影响 \(u\) 是否存活,不需要关心他们,所以不需要记在 dp 状态中。删掉这条边之后,\(v\) 子树内的边如何删就会影响 \(u\),所以要依靠这个设计 dp 状态。

基于这个分析,可以想到一个 dp 是 \(f_{u,i}\) 表示 \(u\) 子树内,满足 “当前子树的根 \(u\) 最后代表的点,和删去从后往前数的 \(i\) 条边之前所代表的点相同” 的概率是多少。

然后考虑树形 dp 的合并,分为两部分,一部分是给子树的 dp 算上到子树的那条边的影响,另一部分是将子树合并上来。

  1. 第一部分,假设现在要 \(f_v\)\(g_{v,j}\) 的贡献(\(g_v\) 就是算上子树头顶上那条边之后,并且根换成了 \(u\)\(f\))。枚举这条边 \((u,v)\) 是倒数第 \(i\) 条删除的:

    • \(i>j\)\((u,v)\) 无论如何删都不会影响结果,因为 \(g_{v,j}\) 并不考虑从后往前数 \(j+1\) 及以前的那些边是怎么删的,所以贡献就是 \(f_{v,j}\)
    • \(i\leq j\):要保证 \((u,v)\) 这条边删去之后 \(u\) 所代表的点不变,所以贡献是 \(\frac{1}{2}f_{v,i}\)
  2. 第二部分,将子树 \(g_{v}\) 合并到当前子树 \(f_{x}\) 中:

    • 假设要合并到 \(f'_{x,i}\),枚举最后删的 \(i\) 条边在 \(g_v\) 中有 \(j\) 条,在 \(f_{x}\) 中有 \((i-j)\) 条。令 \(a=size_x-1-(i-j),b=(i-j),c=size_{v}-j,d=j\),那么 \(g\)\(f\) 合并就需要前 \((a+c)\) 条边是 \(f_x\) 的前 \(a\) 条边和 \(g_v\) 的前 \(c\) 条边排列而成,并且它们内部的顺序不能改变;后 \((b+d)\) 条边同理。那么这种情况的概率就是

      \[\frac{\binom{a+c}{a}\binom{b+d}{b}}{\binom{a+b+c+d}{a+c}}f_{x,i-j}g_{v,j} \]

后者是个树形背包。前者可以前缀和优化一下。加上枚举根,总复杂度就是 \(\mathcal{O}(n^3)\)

1038F | 2900 | B

\(m=|s|\)

\(f_{i,l,r,k,0/1}\) 为填了 \(t\) 的前 \(i\) 个字符,\(l,r\)\(t[1,i]\) 的最长的前缀满足 \(t[1,r-l+1]=s[l,r]\)\(k\) 是最大的 \(k\) 满足 \(t[i-k+1,i]=s[1,k]\)

枚举一下下一步选的是 \(0/1\)\(k,l,r\) 的变化都能够通过 kmp 预处理出。对于合法的状态统计一下方案即可。

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

995F| 2700 | B

dp,\(f_{x,i}=f_{x,i-1}+\prod f_{v,i}\),是个多项式,于是算出点权不超过 \(1,2,\cdots,n+?\) 然后插值即可。

981H | 3100 | D

考虑被 \(k\) 条路径经过的一定也是一个路径。然后在这个路径为外,那 \(k\) 条路径就不能有公共边了。

随便钦定一个点为根。令 \(f_x\)\(x\) 子树内选出 \(k\) 个点并且这 \(k\) 个点到 \(x\) 的路径没有公共边,其方案数是多少。\(g_x\) 同理,是 \(x\)\(x\) 子树外走路径,没有公共边的方案数。

那么答案就是没有祖先关系的 \(x,y\)\(f_xf_y\) 之和,加上 \(y\)\(x\) 子树内的 \(g_xf_y\) 之和。求出 \(f,g\) 之后这些都很好算。

那考虑如果暴力计算 \(f\),是不是一个 01 背包,它看起来很完蛋,但是只需要对物品个数 \(\geq k\) 的进行一个 01 背包,而一共只有 \(n\) 个物品。那么现在暴力做的复杂度是 \(\mathcal{O}(nk)\)

但是 01 背包可以分治 NTT 优化。即:

\[F_x=\prod_{v\in son(x)}(1+size_vz) \]

然后 \(f_x\) 就是 \(\sum k^{\underline i}[z^i]F_x\)

\(t\)\(x\) 的儿子个数,那么分治 FFT 算这个东西,就是 \(\mathcal{O}(t\log^2 t)\)

但是算 \(g\) 就麻烦了,因为算 \(g\) 时单项式个数是父亲的度数。后面就不会了。没想到的点是,其实并不需要知道 \(\sum k^{\underline i}[z^i]G_v\),可以在 \(x\) 处直接将子树中所有 \(v\) 的贡献一起计算。写出来大致就是(\(s_v\)\(v\) 子树内所有 \(f\) 的和):

\[\begin{aligned} &\sum_{v\in son(x)}s_v\sum _ik^{\underline i}[z^i](1+(n-size_x)z)\prod_{v'\in son(x),v'\neq v}(1+size_{v'}z) \\ =&\sum _ik^{\underline i}[z^i](1+(n-size_x)z)\sum_{v\in son(x)}\left(f_v\prod_{v'\in son(x),v'\neq v}(1+size_{v'}z)\right) \end{aligned} \]

最后一个 \((1+(n-size_x)z)\) 可以最后暴力 \(\mathcal{O}(t)\) 乘,问题是求出后面那个多项式。

这个大力分治 FFT,假如将儿子进行重标号为 \(1\sim t\),那么计算 \(F_{l,r}=\prod_{i=l}^r(1+size_iz),G_{l,r}=\sum_{i=l}^rf_i\prod_{j=l}^r[j\neq i](1+size_jz)\)

合并就是 \(F_{l,r}=F_{l,mid}*F_{mid+1,r}\),还有 \(G_{l,r}=G_{l,mid}*F_{mid+1,r}+F_{l,mid}*G_{mid+1,r}\)

于是处理祖孙之间答案复杂度就是 \(\mathcal{O}(t\log^2 t)\).那么总复杂度就是 \(\mathcal{O}(n\log^2 n)\)

是分治 FFT 的套路吗?

930E | 2900 | C

看到题就先容斥。然后容斥系数太难算了就寄了,大概要分好几种情况讨论,于是就弃了。

不容斥也能做。考虑限制将串划分成了若干段,然后一段一段 dp.

有没有什么好的方法描述这个性质?这里考虑的是,如果强制 \([l,r]\) 至少出现一个 \(1\),那么就让 \(r\) 及以后的点,让它们往前找第一个 \(1\) 的位置要 \(\geq l\)\(0\) 也类似计算一个这个东西。

然后 dp,\(f_i,g_i,h_i\) 分别表示,第 \(i\) 个端点结尾的这一段,全选 \(0\),全选 \(1\),既有 \(0\) 又有 \(1\) 的方案数。即有转移 \(h_i=(2^{len_i}-2)(f_{i-1}+g_{i-1}+h_{i-1})\)

\(f\) 的转移就是枚举上一个 \(0\) 在哪一段里面,那么这个就是一个后缀的 \(g\)\(h\) 的和。前缀和优化转移即可。\(g\) 的转移也是类似的。

Code

posted @ 2023-02-23 16:55  do_while_true  阅读(48)  评论(0编辑  收藏  举报