Exemplary Examples IV

\[\Huge\mathrm{Exemplary\ Examples\ IV} \]

导语见 Exemplary Examples I。

T-101 一些组合符号化题目 25-12-31 S+

应当学习一些必要的科技。有一些题目不用科技也能做,但是如果你会科技,就会更加容易。况且,有一些在科技中运用的 OI 思想,是可以迁移的。T-101 中有好多思维跳步大的题目,所以给到 S+,是第一个 S+ 题。

先给最重要的几个式子。在定义新对象的乘除加减的时候,一般会让它们成立。一个是高中数学错位相减法推出来的

\[\sum_{i=0}^{+\infty} A^i=\frac{1}{1-A} \]

另一个是自然对数的定义“某个满足 \(\frac{\mathrm{d}}{\mathrm{d}A}(e^A)=(e^A)\) 的函数”。只要满足它,就是 \(e^A\)

\[e^A=\sum\frac{A^k}{k!} \]

这样就能定义反函数 \(\ln(e^A)=A\)。由于第二个式子成立,可以推出以下式子成立

\[e^{\sum A_i}=\prod e^A_i \]

在这些式子中,\(A\) 就是“新对象”。可以是数字,也可以是多项式,甚至可能是别的什么东西。注意,有时候写 \(e^A\) 会写成 \(\mathrm{exp}(A)\),这两个式子的含义是一样的。

组合对象:树、图、串等。组合对象有一个大小 \(|a|\),定义就是其中的节点个数等。组合对象的乘法是拼接,例如组合对象“字符串 ab”与“字符串 opq”的乘积就是“字符串 abopq”。显然其结果的大小,会是原有两个组合对象大小之和。定义组合对象乘法单位元 \(\epsilon\),它和任何组合对象的乘法运算结果,是另一组合对象本身。特别地,\(\epsilon\times \epsilon=\epsilon\)。你可以把 \(\epsilon\) 看成“空组合对象”。

组合类:组合对象的集合。例如“所有节点个数不超过 100 的二叉树”就是一个组合类。组合类的加法是集合不交并,若集合有交,则运算未定义。组合类的乘法是,在第一个集合内取一个元素,与第二个集合内的一切元素做拼接,得到一个新组合类。将第一个元素取任意值得到的那些组合类,加起来的结果。例如字符串类“0,01,012”和“234,14”的乘法结果为“0234,014,01234,0114,012234,01214”。

在后面的运算中,有时把组合对象和组合类的概念混淆了。这种情况下,不妨把组合对象当成只有一个元素的组合类来理解。

组合构造

sequence 构造:根据某组合类,在其中选择若干组合对象(可以重复),依次拼接形成一个序列,求一切序列构成的组合类(注意措辞“拼接”,这意味着所谓“序列”依然是组合对象做乘法的结果,“序列”和原有的组合对象,属于同一种组合对象。原有组合类内的对象都是字符串,则 sequence 构造得到的组合类内对象,还是字符串)。假设原有组合类是集合 \(\mathcal{A}\),则 \(\mathrm{seq}(\mathcal{A})=\epsilon+\mathcal{A}+\mathcal{A}\times \mathcal{A}+\mathcal{A}\times \mathcal{A}\times \mathcal{A}+...\)。要求 \(\mathcal{A}\) 中不能包含 \(\epsilon\),否则加法未定义。

multiset 构造:根据某组合类,在其中选择若干组合对象(可以重复),组成一个可重集,求一切可重集构成的组合类(对于“集合”的定义是,将“集合”内所有组合对象依字典序排序并顺次拼接,得到新组合对象,这个组合对象就象征“集合”。也就是说,“集合”和原有组合对象,还是同一种组合对象)。假设按照字典序将组合类 \(\mathcal{A}\) 内元素排序后的第 \(i\) 个为对象 \(\mathcal{A}_i\),则 \(\mathrm{multi}(\mathcal{A})=\epsilon+\prod_i(\sum_j (\mathcal{A}_i)^j)\)\(\mathcal{A}\) 中不能包含 \(\epsilon\)

powerset 构造:根据某组合类,选择组合类的某个子集,求一切子集构成的组合类。则 \(\mathrm{pwset}(\mathcal{A})=\prod_i(\epsilon+\mathcal{A}_i)\)\(\mathcal{A}\) 中不能包含 \(\epsilon\)

递归定义

它和 dp 联系很紧密。用 dp 的思想去理解会比较容易。

举例说,组合类“一切 01 字符串”\(\mathcal{S}\),可看成是 \(\epsilon+\{0,1\}\times \mathcal{S}\)。组合类“一切 儿子有顺序的有根二叉树”(可认为是:选取一无标号有根二叉树,然后选取一种 dfs 序,其综合选取方式)\(\mathcal{T}=\epsilon+\mathcal{T}\times p\times \mathcal{T}\)。这里的 \(p\) 代表单个节点构成的二叉树,在此处的意义就是“根节点”。组合类“一切无标号有根树”\(\mathcal{B}=\epsilon+p\times \mathrm{multi}(\mathcal{B})\)

T-101-1 生成函数

对无穷序列 \(a\),定义以 \(x\) 为主元的多项式 \(\sum a_ix^i\) 为它的普通型生成函数;定义多项式 \(\sum a_i\frac{x^i}{i!}\) 为其指数型生成函数。你注意,不管是什么类型的生成函数,它得先是个多项式,定义生成函数 \(A\)\(i\) 次项的系数为 \([x^i](A)\),则普通型有 \([x^i](A)=a_i\),指数型有 \([x^i](A)=\frac{a_i}{i!}\)。两个生成函数 \(A,B\) 之间的加法、乘法,就定义为其对应多项式的加法、乘法;生成函数的幂次、求导、积分等,也是对应多项式的操作。

当然,还可以定义多元生成函数,给定 \(a,b,c,...\),则以 \(x,y,z,...\) 为主元的多项式 \((\sum a_ix^i)(\sum b_jy^j)(\sum c_jz^j)\) 为它的普通型生成函数。

生成函数的作用是什么呢?处理组合构造中的计数问题。这个计数一般是跟组合对象的大小有关(问大小为 \(k\) 的组合对象有多少)。对于组合类 \(\mathcal{A}\),定义其中大小为 \(i\) 的组合对象个数是 \(a_i\),根据 \(a\) 得到对应的普通型生成函数或指数型生成函数 \(A\)。那么对组合类做加法、乘法、组合构造,得到的新组合类对应的生成函数,这个新生成函数可以完全依赖于参加运算的组合类对应的生成函数,而不依赖组合类本身。你所问的,就是最终组合类对应的生成函数的 \(k\) 次项系数。容易得到以下这几个,组合类运算及其生成函数运算的关系。注意这里的都是普通型生成函数:

组合类 \(\mathcal{C}=\mathcal{A}+\mathcal{B}\),则生成函数 \(C=A+B\)
组合类 \(\mathcal{C}=\mathcal{A}\times \mathcal{B}\),则生成函数 \(C=A\times B\)
组合类构造 \(\mathcal{B}=\mathrm{seq}(\mathcal{A})\),则 \(B=\sum A^i=\frac{1}{1-A}\)
组合类构造 \(\mathcal{B}=\mathrm{multi}(\mathcal{A})\)\(B=\prod_i(\sum_j x^{|\mathcal{A}_i|j})=\prod_i(\frac{1}{1-x^{|\mathcal{A}_i|}})=\prod_j(\frac{1}{1-x^j})^{[x^j](A)}\)
组合类构造 \(\mathcal{B}=\mathrm{pwset}(\mathcal{A})\)\(B=\prod_i(1+x^{\mathcal{A}_i})=\prod_j(1+x^j)^{[x^j](A)})\)

对于一个无穷求和的多项式,如果它是收敛的,我们一般可以把它用非无穷求和的方式表现出来,称之为对应生成函数的“封闭形式”。例如对于序列 \(a_i=1\) 的普通型生成函数,它形如 \(1+x+x^2+...\),可得它的封闭形式 \(\frac{1}{1-x}\);对于该序列的指数型生成函数,它形如 \(1+\frac{x}{1}+\frac{x^2}{2}+\frac{x^3}{6}\),通过“最重要的式子”中 \(e^x\) 的定义,得其封闭形式 \(e^x\);序列 \(a_i=\binom{n}{i}\) 的普通型生成函数之封闭形式则是 \((1+x)^n\)\(a_i=\frac{1}{i}\) 的普通型等价于 \(-\ln(1-x)=\ln(\frac{1}{1-x})\)(这也是 ln 的定义,使之生效的);\(a_i=\binom{n+i-1}{n-1}\) 的普通型等价于 \((\frac{1}{1-x})^n\)

封闭形式的好处在于,当对生成函数做操作时,人或者计算机,可以不用处理无穷求和,而是去计算有穷的式子。先看看有哪些对生成函数的常见操作,以及它们对于 \(a_i\) 的影响之组合意义:

加法就是对位相加。
普通型生成函数的乘法 \(C=A\times B\)。这意味着 \(c\)\(a,b\)卷积
指数型生成函数的乘法 \(C=A\times B\)\(c_i=\sum_j\binom{i}{j}a_jb_{i-j}\)

根据这两个乘法操作,我们可以选用合适的生成函数解决 dp 问题。例如若转移式类似于 \(f_i=\sum \binom{i}{j}f_{i-j}g_j\),就宜选用指数型生成函数,写出结果后用多项式计算就好了。

普通型生成函数的移位 \(B=xA\),这意味着 \(b_i=a_{i-1}\)。若左移位,则 \(B=\frac{A}{x}\)
指数型生成函数的移位 \(B=\int A\),这意味着 \(b_i=a_{i-1}\)。若左移位,则 \(B=\frac{\mathrm{d}}{\mathrm{d}x}A\)
普通型生成函数的系数乘 \(i\) \(B=x\frac{\mathrm{d}}{\mathrm{d}x}A\),这意味着 \(b_i=ia_i\)
普通型生成函数的前缀和 \(B=\frac{1}{1-x}A\)。若差分,\(B=(1-x)A\)

一般来说,根据题意写出初始展开形式,以及通过哪些操作来得到最终形式,这并不难。只需灵活应用交换求和顺序。不过一定要牢记,要保持收敛性一致:若对于任意 \(n\),其前 \(n\) 项系数均收敛,那么一个无穷求和收敛。需要关心的是,生成函数的封闭形式和其展开形式的互相转化。

先看展开形式转封闭形式。

公式法。最基本的五个公式已经举过例子了。将陌生生成函数转化为若干熟悉生成函数的组合(就是用“生成函数常见操作”去做转化),然后将熟悉生成函数各自直接应用公式法。特别是如果遇到组合数,想办法给它凑一个 \(\binom{i}{j}x^j\) 或者 \(\binom{i}{j}x^i\) 并应用公式法。

换元法。例如普通型生成函数 \(\sum c^ix^i=\sum (cx)^i=\frac{1}{1-cx}\)

取对数。多项式也可以求 \(\ln(A)\)\(e^A\)(“多项式 ln,多项式 exp”),如果求其前 \(n\) 项的系数,则也只需用到 \(A\)\(n\) 项的系数。时间复杂度仅为 \(O(n\log n)\)(用 NTT,NOI 应该不会考),也有 \(O(n^2)\) 的递推方法,后面讲“求导法求递推式”时介绍。其意义在于把复杂的求积式化为和式。注意,多项式 ln 要求 \(a_0=1\);多项式 exp 要求 \(a_0=0\),否则做不了。

这样一来,multiset 构造有快速求法:\(B=\prod_j(\frac{1}{1-x^j})^{[x^j](A)}\),取对数得到 \(B'=\ln B=\sum_j [x^j](A)\ln(\frac{1}{1-x^j})\)。换元得 \(B'=\sum_j[x^j](A)j\sum_k \frac{x^{jk}}{k}\)。可以 \(O(n\log n)\) 暴力求 \(B'\)\(n\) 项系数,能总复杂度 \(O(n\log n)\)\(B\)。不过还能让形式更好看一点,换求和顺序 \(B'=\sum_k\frac{1}{k}\sum_j[x^j](A)x^{jk}=\sum_k\frac{A(x^k)}{k}\)。这里 \(A(x^k)\) 代表,把 \(x^k\) 换元 \(x\) 代入多项式 \(A\)。这样,就更显然地得到 \(O(n\log n)\) 的求法。至于 powerset 构造也类似,总之就是先取对数,再用 \(e^{B'}\) 还原。

再看封闭形式转展开形式。

法一。

第一步,分子分母分离。分子上的多项式如果不复杂,就利用“乘以 \(x\) 代表位移”来处理;如果复杂,可以整个提出来,利用公式法直接转化回展开形式;如果不能直接转化,把分子分母同除以,分子多项式的某个因式,可使得分子多项式变得简单。至于分母,用下面介绍的方法处理。最终把分子和分母的展开形式,卷一下就好。

第二步,处理分母。式子 \(\frac{1}{F(x)}\)\(F(x)\) 做因式分解,得到 \(\prod (\frac{1}{x-x_i})^{p_i}\) 的形式(重根必须用 \(p_i\) 表示重几次)。这就表示成了若干能应用公式法的因式的乘积,已经可以对它们的展开形式做卷积,而得到结果了。另外,容易证明,它能够被裂项\(\sum \frac{Q_i(x)}{x-x_i}\),其中 \(Q_i\) 是一个至多某次的多项式。用待定系数法,根据所有 \(Q\) 的前 \(j\) 项系数递推求它们的第 \(j+1\) 项系数,即可。

法二,求导法得系数递推式。

其实就是解微分方程。与之所处理问题相近的还有多项式牛顿迭代,但是我没学。

例如,给定 \(A\)\(n\) 项系数,问如何 \(O(n^2)\) 得到 \(B=e^A\) 的前 \(n\) 项系数。对封闭形式求导,得 \(AB=B'\)。对展开形式求导,得 \(B'\)\(B\) 各项系数移位并乘常数的结果。递推地,若已经求出前 \(n\)\(B'\),根据展开形式求导,就直接得到第 \(n+1\)\(B\);据封闭形式求导,\([x^{n+1}](B')=\sum_i [x^i](A)[x^{n+1-i}](B)\),就可以求出第 \(n+1\)\(B'\)。所以就得到一个 \(O(n^2)\) 递推算法。

换句话说,通过对封闭形式求导,来得到导函数 \(B'\)\(B,A,A'\) 之间的第一个关系。通过对展开形式求导,得到 \(B',B\) 间的第二个关系。据这两个关系,看到每个系数,写出无穷个方程,然后从次数最低的系数开始,依次解起。

多项式求 ln 也完全一样。至于多项式求逆,就不用求导了,可直接递推做。因为 \(AA^{-1}=1\),所以 \([x^i](AA^{-1})=[i=0]\)。存在多项式逆元的要求是 \(a_0\neq 0\)。据这个式子递推即可。这样,这几个常见多项式操作,都用递推 \(O(n^2)\) 解决了。

法三,拉格朗日反演。

\(f,g\) 互为复合逆(只要 \(f(g(x))=x\)\(g(f(x))=x\) 成立即可,因为有一个成立能推另一个),则

\[[x^n](H(f(x)))=\frac{1}{n}[x^{n-1}]\left(H'(x)\left(\frac{x}{g(x)}\right)^n\right) \]

求复合逆的办法,也可以写出方程、递推系数。不过更常见的是存在一个关于 \(f\) 的等式,这就可以把等式两边放一边,而另一边是 \(x\),据此得到 \(g(x)\),见下面例题。存在复合逆的要求是 \(f_1\neq 0\)

T-101-1-1

有标号的 \(n\) 个小球,放进任意多个有标号盒子,求方案数。放进盒子的方式可能不止一种。具体地,把 \(i\) 个小球放进某盒子的方式有 \(f_i\) 种。特别地,保证 \(f_0=0\),即不能空放。

若 dp,定义状态 \(g_i\) 表示 \(i\) 个球的答案,则式子是 \(g_i\gets \sum_j \binom{i}{j}f_jg_{i-j}\)。这完全就是指数型生成函数做乘法时的形式。定义指数型生成函数 \(G=\sum g_i\frac{x^i}{i!},F=\sum f_i\frac{x^i}{i!}\)\(G=1+F+F^2+...=\frac{1}{1-F}\),可以多项式求逆得结果;答案就是 \(n![x^n](G)\)

若放入无标号盒子怎么办呢?那就有 \(G=\sum \frac{F^k}{k!}=\mathrm{exp}(F)\)。若是无标号小球放有标号盒子,怎么办呢?重写 dp 式子,这回近似于普通型做乘法的形式,改为定义普通型生成函数就行了。若是无标号小球放无标号盒子怎么办呢?

这个还不好办。直接定义普通型生成函数 \(G=\sum g_ix^i,F=f_ix^i\),它不满足 \(G=\mathrm{exp}(F)\),因为可能存在多个盒子,里面放的球个数相同,你的那个“除以 \(k!\)”除多了。类似于 multiset 构造中的想法,把球个数一样的盒子,一次性确定。得到 \(G=\prod (\frac{1}{1-x^i})^{f_i}\)。当初,快速算 multiset 构造的两种办法中,你选一个就能 \(O(n\log n)\) 解决了。

T-101-1-3 P2767

显然,组合类 \(\mathcal{T}=p\times(\epsilon+\mathcal{T})^m\)。定义对应生成函数为 \(T\),则 \(T=x(1+T)^m\) 只需求 \([x^n](T)\)。运用拉格朗日反演。把等式两边放一边,\(\frac{T}{(1+T)^m}=x\),构造复合逆 \(G\) 只需满足 \(G(T)=x\),因此 \(G=\frac{x}{(1+x)^m}\)。拉格朗日反演:

\[[x^n](T)=\frac{1}{n}[x^{n-1}]\left(\frac{x}{G}\right)^n=\frac{1}{n}[x^{n-1}](1+x)^{nm}=\frac{\binom{nm}{n-1}}{n} \]

T-101-2 幂级数与集合幂级数

可以把幂级数想象成一种“map<?,int> 映射”,用 \(x\) 作为“占位符”,把映射用 \(\sum \mathrm{val}x^{\mathrm{key}}\) 来表示。

举一个例子,定义组合类 \(\mathcal{S}\)“一切用 \(1\times 2,2\times 1\) 两种砖块铺 \(2\times i\) 平面的方法”,把第一种砖块用 \(p\) 代替,第二种砖块用 \(q\) 代替,把两个第一种砖块摞起来得到的 \(2\times 2\) 方块用 \(p^2\) 代替。则 \(\mathcal{S}=\epsilon+\{p^2,q\}\times \mathcal{S}\)。定义组合类 \(\mathcal{A}=\{p^2,q\}\),则 \(\mathcal{S}=\mathrm{seq}(\mathcal{A})=\frac{1}{1-\mathcal{A}}\)。那么这个式子包含了有关 \(\mathcal{S}\) 的一切信息,但一般不需要那么多,我们一般只关心“大小”“个数”。

例如需要求,“用了 \(i\)\(p\)\(j\)\(q\) 得到的方法总量”,怎么办呢?把一个组合类的定义从“集合”改成“map<pair<int,int>,int> 映射”,用 \(\sum a_{i,j}x^{i,j}\) 表示组合类内“用了 \(i\)\(p\)\(j\)\(q\) 得到的方法总量”为 \(a_{i,j}\)。这样,\(\mathcal{A}=x^{2,0}+x^{0,1}\)。故 \(\mathcal{S}=\frac{1}{1-x^{2,0}-x^{0,1}}\)。这实际上就是一个二元生成函数而已。

因此生成函数只是一种特殊的幂级数,不过它最常用。在幂级数里,集合幂级数也比较重要,比较常用。它是“map<sta,int> 映射”,是二进制状态到整数的映射。集合幂级数的加法就是同次数对位加,但乘法被定义成“次数上做某种二进制卷积,系数上做乘法”,最常见的是“次数上做子集卷积,系数上做乘法”。下面重点介绍这类集合幂级数。

最基础的是集合幂级数 exp。也就是定义 \(\mathcal{A}=\sum a_sx^s\),其中 \(a_s\) 表示状态 \(s\) 的“权值”。定义 \(\mathcal{G}=\sum g_sx^s\)\(g_s\) 表示把 \(s\) 划分为若干子集部分的所有方案之加权和,而一种方案的权值是,划分方案中所有子集的 \(a_t\) 之积,即 \(g_s=[x^s](\sum \frac{\mathcal{A}^k}{k!})\)。快速求 \(\mathcal{G}\) 和 FMT 子集卷积的做法完全相同。设状态 \(f_{i,s}=[|s|=i]a_s\)\(h_{i,s}=[|s|=i]g_s\)。把 \(f_{:,s},h_{:,s}\) 看作最高 \(n\) 次的普通型生成函数,对 \(f\) 做 FMT 得到 \(f'\)(高维前缀和要调用加法,直接改成生成函数的加法),

\[h'_{i,s}=\sum_j \frac{1}{j!}\sum_{a_1+a_2+...+a_j=i} \prod f'_{a_k,s} \]

也就是说,\(h'_{:,s}=\mathrm{exp}(f'_{:,s})\)。求出之后 IFMT 回去得到 \(h\),而 \(h_{|s|,s}=g_s\),总复杂度 \(O(n^22^n)\)。与之相应地,做集合幂级数 ln,集合幂级数求逆也都一样。总之就是把集合幂级数改成 \(O(2^n)\) 个以“状态大小”为主元的生成函数,然后做一遍或 FMT,对结果分别做 exp,ln 或者求逆元,并 IFMT 回去。这是因为子集卷积作为此类集合幂级数的乘法,能够通过 FMT 转化成,对位的多项式卷积。故在 FMT 之后,完全依照常规多项式的做法做就是对的。

T-101-2-1

从这道题开始,我们介绍若干道状压图问题。这类问题一般有两个解决方法,两个方法常常联合使用。一个是,用总数减不合法。另一个是,从子结构拼成大型结构。dag 计数问题中,常使用“当前所有入度为零的点”作为子结构;点/边双连通分量/强连通分量在最优化问题中,常用耳分解为子结构,连通分量本身作为大结构,配合“最后一个操作没有结束”;计数问题中,常用连通分量本身作为子结构,而大型结构则成了缩点树/圆方树/缩点 dag。不过无论哪种图,无论什么做法,总是有一维状态是“只考虑 \(s\) 这个点子集内部的边”,如何如何。常见的想法还有,枚举“子集内编号最小的节点所在连通块”的状况。

特别地,有一类问题可以先求出“子图内什么都不要求”的集合幂级数 \(G=\sum 2^{e_{s,s}}x^s\)\(e_{s,t}\) 表示两个集合之间的边数。然后找出,从所求答案的集合幂级数推到 \(G\) 的过程(用集合幂级数运算表示)。那么求答案只需倒推回来就行了。有时还会定义 \(G\) 表示“子图什么都要求,就是不要求连通”的集合幂级数。

T-101-2-1-1

给一张 \(n\le 18\)\(m\) 边的图,多次询问,在某个点子集的导出子图内选择某些边,使得该子集连通的方案数。又称连通子图计数。

法一,常规 dp。总数减去不合法,不合法情况是子集碎成若干连通块,“碎成若干连通块”可以通过枚举“最小编号点所在块状态”来处理。\(f_s=2^{e_{s,s}}-\sum_{t\subset s,\min t=\min s}2^{e_{s-t,s-t}}f_t\)。这里 \(\min s\) 表示子集内最小编号点。优化,注意到后面是一个半在线子集卷积。即可 \(O(n^22^n)\)

法二,“子图内什么都不要求”集合幂级数为 \(G=2^{e_{s,s}}x^s\)。定义集合幂级数 \(F\) 表示连通子图方案数(所求答案的集合幂级数),则 \(G=\mathrm{exp}(F)\),故 \(F=\ln G\)。也能 \(O(n^22^n)\) 解决原问题。

T-101-2-1-2

连通二分子图计数。

若不要求连通怎么做,枚举黑点和白点的点集,要求内部没有边,中间随便连。假设这样求得的集合幂级数为 \(G\),而实际答案为 \(F\)\(F,G\) 之间不是直接 exp 的关系,因为对一个有 \(k\) 个连通块的二分图,它有 \(2^k\) 种染色法。故 \(G=\mathrm{exp}(2F)\),则 \(F=\frac{\ln G}{2}\)

T-101-2-1-3

连通欧拉子图计数。

若不要求连通怎么做,存在欧拉回路的限制是,每个节点的度数均为零。把每个节点的度数,状压为一个小于 \(2^n\) 的二进制整数。也就是说,给若干整数(一条边看成一个整数),问选择整数的某个子集,异或起来成零的方案数是多少。把每条边当成是一个零一变量 \(x_i\),写出对于每一位的异或方程,高斯消元解方程组,则二的自由元次幂,就是解的个数。当然,这也就是把边插入线性基的过程。最后 ln 回去即可。

T-101-2-2

树计数问题,要么就定一个根上树形 dp;要么就别定根,改成从值域扫描,每次加编号为 \(i\) 的点到当前连通块虚树中。\(f_{i,s}\) 表示“\(s\) 内选边,使得子图怎样怎样,要求点的编号都小于等于 \(i\)”的方案数。

T-101-2-2-1

树子图计数。或者问森林子图计数。第二问显然是第一问的答案做集合幂级数 exp 后的结果。

从值域扫描。定义状态 \(f_{i,s}\) 表示只考虑前 \(i\) 个点内部的边,存在某个连通块长成 \(s\) 这个样子的选边方案数。那么 \(f_{i,s}=\sum_{a_1+a_2+...+a_k+\{i\}=s}\frac{1}{k!}\prod f_{i-1,a_j}\),可以 exp。虽是 \(n\) 次,但是每次只用 exp 前 \(i\) 个点,共计 \(\sum i^22^i=n^22^n\)

T-101-2-2-2

仙人掌子图计数。

仙人掌是什么?缩点之后每个点双都是环的图。环的集合幂级数可以简单 dp 得出。接下来相当于 dp 圆方树形态,关键的是割点。定义 \(f_{i,s}\) 表示“\(s\) 内选边,使得 \(s\) 是仙人掌,要求割点的编号都小于等于 \(i\)”的方案数。初始化 \(f_{0,s}\) 时不能有割点,因此只能是一个环;转移时,\(f_{i,s}=\sum_{a_1+a_2+...+a_k+\{i\}=s}\frac{1}{k!}\prod f_{i-1,a_j+\{i\}}\),可以 exp。总复杂度 \(O(n^32^n)\)

T-101-2-2-3

点双连通子图计数。

\(G\) 表示在 \(s\) 内任意选边的集合幂级数。如何用所求答案 \(F\)\(G\) 呢?相当于 dp 圆方树形态,设状态 \(f_{i,s}\) 表示“\(s\) 内选边,什么也不要求,只要求割点的编号都小于等于 \(i\)”的方案数。初始化 \(f_{0,s}=[x^s]F\)。接下来转移 \(f_{i,s}=\sum_{a_1+a_2+...+a_k+\{i\}=s}\frac{1}{k!}\prod f_{i-1,a_j+\{i\}}\),exp 即可。\(f_{n,s}=[x^s]G\)。如今倒着推回去到 \(f_{0,:}\),每次做 exp 的逆运算也就是 ln。总复杂度 \(O(n^32^n)\)

T-101-2-2-4

边双连通子图计数。

法一,同上设状态 \(f_{i,s}\) 表示“\(s\) 内选边,要求割边的端点的编号都小于等于 \(i\)”的方案数,从 \(f_{i-1,s}\)\(f_{i,s}\) 仍旧容易,只需要考虑所有与 \(i\) 相连的割边的影响即可。而实际上知道 \(f_{n,s}\) 的值,逆回来即可。

法二,注意到割边就是大小为二的点双。那只需求子图“点双缩点后不存在大小为二的方点”的方案数。先做一遍 T-101-2-2-3 预处理出点双的集合幂级数 \(F\),并将大小为二的状态对应的值改成零。接下来类似于 T-101-2-2-2,设状态 \(f_{i,s}\) 表示“\(s\) 内选边,使得子图是边双,要求割点的编号都小于等于 \(i\)”的方案数。初始化 \(f_{0,:}=F\)。转移时,\(f_{i,s}=\sum_{a_1+a_2+...+a_k+\{i\}=s}\frac{1}{k!}\prod f_{i-1,a_j+\{i\}}\),可以 exp。

T-101-2-3

dag 容斥,枚举当前所有入度为零的点集 \(s\) 来转移,容斥系数是 \((-1)^{|s|+1}\)

T-101-2-3-1

dag 子图计数。

由 dag 容斥写出暴力转移式 \(f_s=\sum_{t\subset s}[t 为独立集](-1)^{|t|+1}f_{s-t}\)。注意此处没有“钦定最小标号点在独立集里”。改写为集合幂级数的形式。设集合幂级数 \(G\) 满足,所有 \(s\) 为独立集的位置有 \([x^s](G)=(-1)^{|s|+1}\),其余位置为零。则 \(f_s=[x^s](\frac{1}{1-G})\),集合幂级数求逆即可。

T-101-2-3-2 P11714

强连通子图计数。

用子图任意的方案数,减去子图缩点后超过一个点的方案数(定义它是 \(g_s\))。由 dag 容斥,定义 \(h_s\) 处理“入度为零”层,即“子图缩点后为偶数个相互独立强连通分量的方案数,减去奇数个的”。据此 \(h_s=\sum_{t\subset s,\min s=\min t}(-1)f_th_{s-t}\)。显然 \(g_s=-\sum_{t\subset s,\min s=\min t} h_t 2^{e_{s,s-t}}\),而 \(f_s=2^{e_{s,s}}-g_s\)。精细实现做到 \(O(3^n)\)

T-101-2-3-3 P10221

前面几步都非常简单就不说了,关键在于得到 \(O(n^23^n)\) 做法之后,有两种能够通过该题的方法。一种是卡常。在瓶颈循环中,如果发现某层循环只涉及到 popcount 个元素,或者只用枚举到 popcount 就行了,那一定要写这个剪枝。另外,瓶颈循环里面,取模次数越少越好,最好放到外面预处理需要取模的东西(说题外的,有时瓶颈循环调用组合数。应放弃预处理阶乘逆元的办法,而是选用杨辉三角。减少了取模次数)。

法二。关注瓶颈部分 \(f_{i,s}=\sum_{t\subset s}[\mathrm{chk}(t,s-t)]\sum_j f_{j,t}g_{i-j,s-t}\),其中 \(g\) 是可预处理数组。本来半在线子集卷积就能做,可惜 chk 不可能在 FMT 过程中处理。一旦发现 dp 转移中,存在近似于卷积的形式,就可以生成函数,用代值计算法,插值得到答案。将 \(f_{:,s}\) 看作 \(n\)生成函数,给形式底数 \(x\) 代入 \(n\) 个点值。每代入一次,据上述式子给每个 \(s\) 求一遍 \(F_s=\sum f_{i,s}x^i\)\(O(3^n)\) 的。\(O(n3^n)\) 就能求得 \(n\) 个点值,再插值就能求多项式系数,也就是 \(f_{i,s}\) 具体是多少了。

这个题目和 T-101-1-2 共同提示我们,生成函数首先是一个多项式,其次才具有别的性质。它作为一个多项式,就意味着支持代入点值、插值、一切 NTT 优化的快速多项式计算。另外,一切近似卷积的问题,都可以看作是生成函数(多项式)在做乘法。

T-101-3 容斥与集合划分容斥

我再次强调,计数问题如果直接 dp 不了,或者直接 dp 复杂度无法接受的,可以看看能不能容斥(尤其是答案统计和“极大段”有关的情况下)、组合生成函数。先回顾常规求容斥系数的思路(其实不是回顾,是在发明,因为,好像我对容斥的理解又深化了。修改了好几版)。

给一些元素,每个元素有取值方式,问“所有方案加权和”。这个权值的计算方法如下:

题目给定一族对元素取值的限制 \(R\)。你可以给它定义“严格度”,其中任两个限制的“严格度”之间可能存在偏序关系(具备传递性),也可能不可比。对它的定义必须非常谨慎,使得:对于任一方案,能够唯一找出它在 \(R\) 中满足的最严格的限制 \(x\)(不存在另一个它满足的限制 \(y\) 使得 \(y\)\(x\) 严格),且方案的权值,是只跟 \(x\) 有关的某个已知 \(p(x)\)

记录权值为 \(p(x)\) 的方案共 \(f(x)\) 种,那答案就是 \(f(x)p(x)\),但 \(f(x)\) 极其难算。不过“满足限制 \(x\)”的方案数 \(g(x)\) 极其好算。现在希望给 \(x\) 配上一个容斥系数 \(q(x)\) 使得 \(\sum f(x)p(x)=\sum g(x)q(x)\)。计算方法是,\(q(x)=p(x)-\sum_{y<x}w(y|x)q(y)\),其中 \(w(y|x)\) 表示任一满足限制 \(x\) 的方案,是否一定也满足限制 \(y\)(即,被 \(g(x)\) 统计的方案一定被 \(g(y)\) 统计)。有时可以用 dp 来处理 \(\sum_{y<x}w(y|x)q(y)\)。如果算系数的速度还不够快,就需要打表看规律了。

千万注意,因为偏序关系是你自己定的,所以 \(w(y|x)=[y\ge x]\) 并不总是成立。

反演是指,题目给定限制族,然后你自己给它定一个偏序。现在已知 \(g(x)\) 想要求出 \(f(x)\)。一般是按照偏序关系从小到大枚举 \(x\)\(f(x)=g(x)-\sum_{y<x}v(y|x)\),其中 \(v(y|x)\) 表示同时满足 \(y,x\) 两个限制的方案数有多少,计算 \(v(y|x)\) 的时候可以自选调用 \(f(y)\)(指定的偏序关系要比较合适,使得 \(v(y|x)\) 容易计算)。反演常应用于,题目询问所有 \(f(x)\) 的情况下。用反演,就不能得到那个独立于方案数的系数 \(q\) 了。

反演所能处理的一类问题(如 T-101-3-4),直接容斥做不了吗?对上述容斥做个小扩展即可,只需更改 \(w(y|x)\) 的定义为“概率”(而不是原有的零一离散变量),也就是在满足限制 \(x\) 的条件下,同时满足限制 \(y\)条件概率

集合划分容斥是一个特例。若把全集 \(U\) 恰好划分成元素 \(x_1,x_2,...\) 的权值是 \(p(x)=\prod P_i(x_i)\),方案数是 \(f(x)\),求加权和 \(\sum f(x)p(x)\)。易求出集合至少划分成元素 \(x_1,x_2,...\) 的方案数(也就是说 \(x_i\) 内部可能还有划分)\(g(x)\),想要配上合适权值 \(q(x)=\prod Q_i(x_i)\)。定义幂级数 \(A=\sum_i Q_i(x_i)y^{x_i},B=\sum_i P_i(x_i)y^{x_i},C=\sum_i G_i(x_i)y^{x_i}\)。若存在某组合构造方法对应的幂级数变换 \(H\) 使得 \(g(x)=[y^U]H(C)\)\(\sum w(y|x)q(y)=\prod_i [y^{x_i}]H(A)\)(这两个会同时成立,是“集合划分”的性质保证的),故 \([y^x]B=[y^x]H(A)\)。至于为什么 \(q(x)\) 也可以把多个小限制分开独立计算,最后再乘起来?这是集合划分问题的内禀属性使然(就是说能证,但是我不会证)。集合划分容斥运用生成函数能够解决的问题,我们当然也能拿通用的 dp 方法至少给出一个暴力,具体方式就是考虑所有把一个元素 \(x_i\) 划分成若干其它元素的方案 \(x'\),它是把原来的元素划分成了 \(x'_1,x'_2,...x'_n\),则 \(Q_i(x_i)=P_i(x_i)-\sum_{x'}\prod Q_j(x'_j)\)

题目的限制族不明显时,需要你自己构造合适的族。问题只有一个目标(只有一个特殊的 \(p(x)\) 不为零),则定义限制族时,可以把问题的目标限制(没有怎样的元素/没有怎样的关系)拆成若干单点/双元小限制,然后此限制族的某个大限制就应该是,在所有可能的小限制构成的集合中,挑选一个子集,囊括其中的所有小限制。很特别地,我们常常把所有限制取反面,然后定义限制族,这是为了方便求 \(g(x)\)

一般来说,你指定的偏序关系尽量要使得 \(w(y|x)=[y<x]\) 成立。如果做不到,再勉为其难尝试别的定法,例如 T-101-3-4。

我们最后用这套理论,推理一下数论莫比乌斯函数。此场景下,限制族 \(R\) 中的每个元素都是一个数字 \(x\),用于限制“某元素应该是它的倍数”。若 \(p(x)=[x=1]\) 那么可以推断 \(q(x)=-\sum_{y|x,y\neq x}q(y)\),从而 \(q\) 就是莫比乌斯函数。那么为什么 \(\sum_{d|x}q(d)=[x=1]\) 呢?因为我们在此更换了主体,如果以一个顶格满足限制 \(x\) 的方案为主体,那么它总计被所有 \(d|x\) 的限制计入过,它对答案的贡献系数就是上述 \(\sum_{d|x}q(d)\),而这个东西就是 \(p(x)=[x=1]\)

T-101-3-1 AGC058D

如果直接 dp,必须记录最后两个字符以及剩余的 A,B,C 个数,转移还是十分固定的,虽然不能矩阵快速幂,但是 T-101“组合类的递归定义”告诉我们,如果存在 dp,那么对应的生成函数也存在合理递归定义。那此时能不能设生成函数替代 dp 呢?

法一,直接替代。具体来说,设 \(A_1\) 表示以 AA 结尾合法串的三元生成函数,\(A_1\) 表示 AB 结尾的,...,直到 \(A_9\) 表示 CC 结尾的。于是就能得到一个九元线性方程组,形如 \(A_0=x^2+x(A_0+A_4+A_7)\) 之类(其实就是把 dp 转移写成生成函数形式而已)。方程并不好解,但好像有人能解出来。

法二。因为不会解方程,我们希望得到一个不记录最后两个字符的 dp 状态。先看三个“不能出现子串”的限制。以缝隙为主体(我才讲过啊!序列上的连通块,考虑缝隙。子串也算序列上的连通块嘛)改单点限制“不存在连续的 A|B,B|C;不存在连续 B|C,C|A;不存在连续 C|A,A|B”,那不就是“A|B,B|C,C|A 连续段长度不超过二”吗?容易想到“枚举最后一个连续段的长度”来做 dp,但是不知道倒数第二个连续段的末尾字符,这个 dp 就不对。因为限制被加在极大元素上,尝试容斥。为了让 dp 数组里的值都是对的,因此每次转移都要容斥。

具体来说,定义限制族 \(R_{A,B,C}\) 表示向状态 \(f_{A,B,C}\) 转移时的容斥限制族,限制形如“最后一个连续段长度大于等于 \(x\)”,“严格度”的偏序关系显然就是 \(x,y\) 的数值大小关系。那么 \(p(x)=[x\le 2]\)\(g(x)=\sum f_{A',B',C'}t(x)\)。考虑 \(\sum_{y<x} w(y|x)q(y)\) 是什么,若一个方案满足了“最后一个连续段长度大于等于 \(x\)”,是否也一定满足“最后一个连续段长度大于等于 \(y\)”?好像是的,但其实不是。你注意,“同时满足”的定义是,被 \(g(x)\) 统计的方案一定被 \(g(y)\) 统计,而若 \(y\le x-2\) 则因为 \(f\) 每个状态都是正确的,那么所有被 \(g(x)\) 统计的方案都不被 \(g(y)\) 统计。换句话说 \(q(x)=p(x)-\sum_{y\in[x-1,x]}q(y)\),打表后容易看出并证明 \(q(L)=[L\mod 3=1]-[L\mod 3=0]\)

当然,上述思路是旧版的。用上新版的技巧,我们已经知道这是一个集合划分容斥问题,但是我们就是不用生成函数,就是想打暴力 dp。根据集合划分容斥问题的内禀属性,如果知道了一个划分方案 \(x\) 中所有连续段的长度 \(x_i\),则 \(p(x)=\prod P(x_i),q(x)=\prod Q(x_i)\)。则令 \(H(i)=\sum_{1\le j<i} H(j)Q(i-j),Q(i)=P(i)-H(i)\)。算出 \(Q(i)\) 后再修改对应 \(H(i)\) 的定义,令它额外加上 \(Q(i)\)。也可得到上述结论。

法二点五,上集合划分容斥。显然幂级数应该以连续段长度为次幂,因此 \(H\) 就是个 sequence 构造而已,有 \(\frac{1}{1-A}=B=x+x^2\),多项式求逆即可。多项式求逆也是可递推的过程,能归纳出递推式 \(a_i=-a_{i-1}-a{i-2}\),容易证明 \(Q(L)=[L\mod 3=1]-[L\mod 3=0]\)

得到该容斥系数后,用递归定义的三元生成函数替代 dp(用 \(x,y,z\) 的次方分别代表 A,B,C 出现的次数),那么答案函数 \(R=(x+y+z)\sum_{i=0}(xyz)^iR-3\sum_{i=1}(xyz)^iR=\frac{1-xyz}{1-x-y-z+2xyz}\)。最终答案就是 \([x^Ay^Bz^C](1-xyz)(\sum (x+y+z-2xyz)^k)\)

归约到求 \([x^Ay^Bz^C]\sum (x+y+z-2xyz)^k\)。枚举 \(k\) 之后,还是不敢暴力卷积。关键在于答案只要一项,没必要把整个多项式算出来。那么类似于 T-096,把“多项式乘开”的过程,看作是在 \(x,y,z,-2xyz\) 中选择一个乘到式子里,共选 \(k\) 次,并把得到的 \(4^k\) 个式子求和。改为枚举途中选了多少次 \(-2xyz\),为了让 \([x^Ay^Bz^C]\) 这一项有系数,\(k\) 只能取一个确定的值,就能组合数直接算了。

T-101-3-2 QOJ11704

不可能按序列依次决定当前最后一个颜色段是什么颜色、长度是多少。只能按颜色 dp,每次需要枚举把当前颜色划分为多少段,并把当前这些颜色段插入到先前的若干颜色段中。但是插入方式很有可能让两个“被划分为不同段”但同颜色的段相邻,这就不对了。用容斥解决,定义限制族 \(R\) 中的限制 \(x\) 对应的若干小限制 \(x_i\) 形如“要求环上 \([l,r]\) 颜色相同”,并要求所有 \(X_i\) 的并集是全集。那么“严格度”偏序关系显然就是,若限制 \(y\) 的任一小限制 \(y_i\) 都被某个 \(x_i\) 包含,则 \(y<x\)。那么,\(p(x)=\prod |x_i|\),这满足了“特别情况”,定义 \(p(x)=\prod P(x_i),P(x_i)=|x_i|\)\(q(x)=\prod Q(x_i)\),显然 \(Q(x_i)\) 只跟 \(|x_i|\) 有关,修改定义为 \(q(x)=\prod Q(|x_i|)\)。想知道 \(W(Y'(|x_i|)|(|x_i|))\prod_{|y_i|\in Y'(|x_i|)}Q(|y_i|)\) 的求法,可以 dp。设它为 \(S(|x_i|)\),则有递推式 \(S(L)=\sum S(L-i)Q(i)\)

但是这个限制族太庞大,不可能 dp 一个一个求 \(g(x)\),这要求我们把 \(q(x)\) 相同的限制合并起来。定义 \(|x_i|\) 构成的可重集为 \(s(x)\),则 \(\sum g(x)q(x)=\sum_{s(x)}g'(s(x))q'(s(x))\)。转化之后就容易 dp 求了,定义状态 \(f_{i,j}\) 表示把前 \(i\) 种颜色的所有元素划成 \(j\) 段的容斥加权和。据此定义辅助状态 \(g_{i,j}\) 表示把 \(i\) 个颜色相同的元素划成 \(j\) 段的容斥加权和,则 \(g_{i,j}=\sum_k g_{i-k,j}Q(k),f_{i,j}=\sum_k \binom{j}{k}f_{i-1,j-k}g_{a_i,k}\),就解决了,吗?

样例过不去。思考一下,当前 dp 算的东西对不对劲。当前 dp 算的是 \(\sum_{x_1+x_2+...=n}(\prod Q(x_i))(给 x_1,... 分配颜色使之合法的方案数)\)。这不对劲,因为这相当于算“序列”的答案,“序列”和“环”的答案不该相同。考虑对任意一种“序列”的实际方案,该方案恰好对应一种“环”。但“序列”的首尾两段可能颜色相同,因而权值和“对应环”不一定相同,算得不对。枚举首段颜色,还有两个值 \(i,j\) 表示首段长度 \(i\) 和“若首尾颜色相同则 \(j\) 表示尾段长度;反之 \(j=0\)”。可惜不知道 \(i,j\) 到底是不是“极长长度”。对此容斥,定义实际系数 \(\hat p(i,j)\) 和容斥系数 \(\hat q(i,j)\),则 \(\hat p(i,j)=i+j\)\(\hat g(i,j)\) 可以通过刚才的 dp 直接求出。\(\hat q(i,j)=\hat p(i,j)-\sum_{k\le i,l\le j}[(k,l)\neq (i,j)]\hat q(k,l)S(i-k)S(j-l)\)

把这些东西全部编到 dp 里面,重新定义 \(g_{i,j,0/1}\) 表示把 \(i\) 个颜色相同的元素划成 \(j\) 段,这里面有没有“首尾段”,其容斥加权和是多少;\(f_{i,j,0/1}\) 表示把前 \(i\) 种颜色的元素划成 \(j\) 段,目前有没有“首尾段”,其容斥加权和是多少。答案就是 \(\sum f_{n,:,1}\)。复杂度瓶颈在求 \(\hat q(i,j)\),是 \(O(V^4)\)

T-101-3-3 P10104

第二条限制不好处理,容斥。定义限制族 \(R\) 中的限制 \(x\) 由小限制 \(x_i\) 组成。\(x_i\) 是状态压缩数字,要求连通子图 \(x_i\)\(b\) 全部相同,且 \(x_i\) 的并集是全集。则“严格度”偏序关系显然就是,若 \(y\) 中的所有 \(y_i\) 均是某个 \(x_i\) 的子集,则 \(y<x\)。容易知道除了 \(x_i\) 均为单点的那个 \(p(x)=1\),其余均为零。现在思考如何求 \(g(x)\)

若连通块大小为偶数,显然该连通块内元素取什么值不要紧,方案数就是块内最小 \(a\) 值。然后把所有大小为奇数的连通块内最小 \(a\) 值找出并依次排开得到长度为 \(k\) 的序列 \(c\),求“有 \(k\) 个数,第 \(i\) 个数小于等于 \(c_i\),使得它们异或和等于 \(C\),问方案数”。数位 dp 能做,复杂度 \(O(k\log V)\),就不提了。把偶数块的最小 \(a\) 的乘积,再乘上这个东西的答案,就得到 \(g(x)\)。那容斥系数 \(q(x)\) 怎么求。容易看出 \(q(x)=\prod Q(x_i)\)

\(Q(x)=[|x|=1]-\sum_y W(y|x)\prod Q(y_i)\),定义后面那一长串是 \(S(x)\),则 \(S(x)=\sum_{y\subset x,\min y=\min x}S(x-y)Q(y)\),转移要求 \(y\) 是连通子图。算完 \(Q(x)\) 后令 \(S(x)\gets Q(x)\) 即可。

用 dp 求 \(\sum g(x)q(x)\)。为了最后算 \(g(x)\),状态必须记录,所在块是奇数块,且是所在块内 \(a\) 最小值的关键位置。故定义状态 \(f_{s,t}\) 表示选择若干连通块使得 \(s\) 内点都被所选连通块包含,且 \(t\)关键位置集合的,“部分加权”(如果转移一个偶数块,就把权直接乘上)方案数。也就是说,

\[f_{s,t}=\sum_{p\subseteq s,\min p=\min s}f_{s-p,t-(\mathrm{argmin}_a p)}Q(p)[|p|=2x]+f_{s-p,t}Q(p)(\min_a p+1)[|p|=2x-1] \]

状态 \(O(3^n)\),瓶颈复杂度 \(O(4^n)\),可以艰难通过。但是还能优化。因为只关心关键位置,关键位置必须是块内 \(a\) 最小值所在处,可以扫描值域,定义 \(f_{i,s}\) 表示依次决定了前 \(i\) 个点所在连通块,以及是否关键,若是关键位置,则 \(s\) 中对应位置染成一;后 \(n-i\) 个点,若和前 \(i\) 个点中的某个在同一连通块,就在 \(s\) 上染成一。其“部分加权”(如果转移一个偶数块,就把权直接乘上)方案数是多少。因此状态优化为 \(O(n2^n)\),瓶颈复杂度 \(O(n3^n)\)

T-101-3-4 P8340

注意到,能够还原信息 \(x\) 的子集,其中元素都小于 \(x\),故不妨按照 \(x\) 从小到大转移。定义状态 \(f_{i,j}\) 表示考察前 \(i\) 个元素是选还是不选,使得恰好 \([1,j]\) 内的信息都能被还原的选取方案数。则 \(f_{i,j}\gets f_{i-1,j}[j\ge i],f_{i,j}\gets f_{i-1,j-i}\)。复杂度 \(O(n^2)\)

用折线图分析,当前位置横坐标为 \(i\) 时,要么向右走一格,要么同时向右走一格、向上走 \(i\) 格。问直到走到横坐标为 \(n\) 为止,没有低于直线 \(y=x\) 的行走方案数。这个不能反射容斥。用总数减去不合法,不合法的情况应该是,存在某个时刻 \(y=x-1\)。不过一条路径很可能存在多个位置使得 \(y=x-1\)

容斥,定义限制族 \(R\) 中的限制 \(x\) 表示“要求折线经过 \((x,x-1)\) 这个点”。但这里面没有“偏序关系”,不妨人为指定一个“直接比较 \(x\) 的大小作为偏序关系”,这样就能满足“一个方案只满足一个‘极大’限制”这个对偏序关系的要求。则 \(p(x)=-1\)\(g(x)=h(x)2^{n-x}\),其中 \(h(x)\) 表示选择 \([1,x]\) 的某个整数子集,子集和恰好为 \(x\) 的方案数。然后你会发现,若 \(y<x\)\(g(y)\) 所包含的方案有一部分属于 \(g(x)\),而另一部分不属于,没有办法定义零一离散的 \(w(y|x)\),只能把它定义成概率。这样定义和直接反演本质相同,写出反演式子 \(f(x)=g(x)-\sum_{y<x}v(y|x)=2^{n-x}(h(x)-f(y)s(y+1,x-1))\)。其中 \(s(l,r)\) 表示选择 \([l,r]\) 的某个整数子集,子集和恰好为 \(r-l+2\) 的方案数。

先想 \(h(x)\) 如何求。我介绍过这个经典问题,复述一遍解法:有若干不同的数字加起来等于 \(x\),则数字种类\(O(\sqrt x)\)。想象有一个直方图,其列数为 \(O(\sqrt x)\),且每一列的高度严格递增,就是问此直方图列高度和等于 \(x\) 的方案数。定义状态 \(f_{i,x}\) 表示 \(i\) 列的直方图高度和等于 \(x\) 的方案数,则有两种转移,一种是让当前所有列都升高一;另一种是在最左侧新开一列,这一列初始高度为一。不允许在中间没有转移一的情况下连开两列。那么 \(O(n\sqrt n)\) 即可求得 \(h(x)\)

这里是半在线的形式,可以 cdq 分治优化。具体来说,若已经求出 \(f(y),y\in[l,\mathrm{mid}]\),如何求出它对 \(f(x),x\in[\mathrm{mid+1},r]\) 的贡献呢?因为求 \(v(y|x)\) 的转移非常相似,不妨统一赋初值,统一转移。

T-101-3-5 P4707

拆贡献,只需求 \(\sum_{t=0}^{\infty} \sum_{i<k} P(在时刻\ t\ 恰好有\ i\ 种出现过)\)。“恰好”难以处理,不妨做容斥变为“求在时刻 \(t\) 至多有 \(i\) 种出现过,或者说至少有 \(n-i\) 种未出现过,也就是说钦定 \(n-i\) 种元素不准在前 \(t\) 秒出现”的方案数 \(g(t,n-i)\)。则原有系数 \(p(t,i)\) 当且仅当 \(i<n-k\) 时为零,反之为一。实际容斥系数 \(q(t,i)=p(t,i)-\sum_{j<i}\binom{i}{j}q(t,j)\)。容易打表看出 \(q(t,i)=(-1)^{i-(n-k-1)}\binom{i-1}{n-k-1}\)

\(g(t,i)=\sum_{方案}(1-\sum_{i\in\ 方案}\frac{p_i}{m})^t\) 不太好求。但是实际要的是 \(\sum_t\sum_i q(t,i)g(t,i)=\sum_i q(i)\sum_t g(t,i)\),而 \(\sum_t g(t,i)=\sum_{方案} \frac{1}{\sum}\)。设计 dp,\(f_{i,j,k}\) 表示考虑前 \(i\) 种元素是否选取,共选了 \(j\) 个元素,当前选取元素的 \(p\) 和为 \(k\) 的选取方案数。这是 \(O(n^2m)\) 的。

无法通过,关键在于 \(k>n-10\) 的性质没有用到。先想着对称一下使得“只有大于 \(n-k\)\(i\) 容斥系数不为零”,但不可行。事实上,一切容斥题目在经过容斥转化后,要解决的都只剩下带权计数问题,可关注权值的组合意义,乘进方案数里。原来的 dp 是在描述“选取了哪些元素”。容斥系数中,负一的那一部分好搞,关键是要把 \(\binom{i-1}{n-k-1}\) 乘进去,这相当于描述“选取了哪些元素,并在这些元素中高亮标出 \(n-k-1\) 个,要求标号最小的元素不得被高亮”的联合方案数,是容易 dp 的,就做完了。

这东西其实就是 kthmax-min 容斥,也就是 min-max 容斥的扩展版本。我们的容斥通法可以推导出它来。

T-101-3-6 P9563

法一,看到限制过强,不妨容斥(恐怕是,一见到限制,就得问问自己,能不能容斥)。定义限制族 \(R\),内部的元素 \(s\) 要求必须包含关键点子集 \(s\)。偏序关系显然是若 \(t\subset s\)\(t<s\)。原系数 \(p(s)=[|s|=0]\),推得 \(q(s)=(-1)^{|s|}\)。关键是如何计算 \(g(s)\),应求出 \(s\) 的最小外接矩形 \(Q_s\),求“所统计的正方形包含这个矩形”的面积和 \(w_{Q_s}\),是一个低次多项式。改变主体,以“最小外接矩形”为主体,合并统一计算。也就是说,\(\sum g(s)q(s)=\sum_Q w_Q\sum_{s}[Q_s=Q](-1)^{|s|}\)。这是一个 \(O(k^4)\) 的做法。

此类容斥题目,很常见的一个优化是,找出系数必定为零的方案,并将其剔除掉。发现一旦 \(Q\) 内非边界位置包含了关键点,则 \(\sum_{s}[Q_s=Q](-1)^{|s|}=0\),因此 \(Q\) 只可能在边界上具有关键点,\(Q\) 的总数是 \(O(n^2)\) 的,直接枚举即可。

法二,其实不用容斥也能做,就是一个“网格图上有障碍物,矩形计数”的 trick,已经被考烂了。这种题目永远是枚举左边界所在列,求出这一列的每个位置向右最多拓展多远,据此建立笛卡尔树,然后分析剩余细节。本题先离散化,之后相邻的横坐标称为 \(x_i,x_{i+1}\)。不能枚举左边界所在列,只能同时计算一个连续段,也就是左边界处于 \(x_i,x_i+1,...,x_{i+1}\) 这些列的答案。好就好在笛卡尔树形态不变,所以容易算。

T-102 一些“坐标轴”题目 26-1-12 S

性质决定做法。不太确定信息是否满足某性质时,可大胆猜结论并验证。先介绍信息的若干常见性质:

交换律。任意交换操作顺序,结果不变。
可逆性。存在逆运算或逆元素,能够快速撤销上一个操作。如果同时存在交换律,就能撤销任意时刻进行的操作。如果撤销操作的成本过高,就应尝试用时间轴操作规避撤销。
可加性。或者说可合并性。就是现有两个集合的全部信息,能否快速求“这两个集合不交并”的信息。尤其关注“可按任意顺序合并性”。
可重算性。字面意思,常见于最优化问题中。
可减性。若 \(t_1,t_2\) 两个集合的不交并结果为 \(t\),那么可减性就表示,有办法在只知道 \(t_1,t\) 全部信息的条件下,快速求得 \(t_2\) 的全部信息。这是差分割补的基础。
单调性。如果两个轴之间满足单调性,那这两个轴的地位基本就等价了。
幂等性。多次操作和只操作一次等价。如果某个元素满足了幂等性,那它就不重要了,大概率可以删除。

注意,上述“全部信息”是指“全部对算答案有用的信息”。为解释上述性质的用途,举一些例子:
单点修改,单点查询,什么性质也无需满足。
单点修改,区间查询,用分治数据结构处理的条件是,所维护的信息必须满足交换律和可加性,也就是说,支持以任意顺序合并得出区间信息。
区间修改,单点查询,用打标记的方法处理的条件是,标记具备交换律和可加性。
区间修改,区间查询,除了以上两点,还需满足:先合并区间信息,再总体作用标记,与先作用标记,再合并区间信息,得到的信息应该等价。

一旦题目中有个“解”字,例如最优化、判定性计数问题,我就能够应用调整法、限制的选取与对称、从边界元素入手、从特殊性质入手等办法,做题就比较顺;但是如果题目中没有“解”字,就比较吃力。没有“解”字的题目应该成为近期的工作重点。其中,大部分数据结构题目就没有“解”字。

现在看来,没有“解”字的题目,有如下普遍做法:从答案去考虑,尝试变换贡献主体。如果有本质相同问题,就尝试递推统一计算。统一计算的元素之间的关系非常的大,大概率是某个维度上的连通块,甚至同时是多个维度上的连通块(事实上,如果元素有种类,我们常常统一计算同一种类的元素;可以把“种类”也视作一个轴)。递推计算的元素之间的关系也非常大,大概率在某个维度上相邻(递推做到极致就是莫队)。如果元素没有用了,就把它删掉或者和别人合并起来。如果某种元素看起来重要,一定一定要分析其个数是不是特别少。

常见的轴有,时间轴、位置轴、值域轴。特别关注“区间”,既可以把它当作一维的东西,也可以把它当作二维的。树上问题可能还有“深度轴”。尽量把“转移顺序明显”的轴放在外面。

最常见的对最外轴的操作,就是“扫描最外层”。假设扫到 \(t\),一般是实时维护“在外部轴时刻 \(t\),内部轴上坐标为 \(i\) 的元素的值,是什么”。如果有多个内部轴,就维护二维平面,可以利用割补法。被割成的若干区域的求法,常常完全不同(可以离线,可以在线;可以改主体,可以递推)。割补法的基础是要知道,在二维平面上,一只老哥足够处理什么。第一个是,在线(带修)求两条平行直线之间范围内的和/最大值;第二个是,在线(带修)求某条直线上的某个点 \((x,kx+b)\) 左上方的和/最大值;第三个是,离线求,定了小于等于三条边的半矩形,内的和/最大值(扫描线求);第四个是,离线求任意矩形内的和/最大值(历史和/历史最值)。如果允许两只老哥或者一只根号,就能用树套树或者 kdt 去在线求任意矩形内和/最大值了。若允许根号,不妨对二维平面分块。而所谓“强制在线”,就意味着题目指定了最外轴是谁,而且钦定最外轴仅允许直接扫描线。

特殊情况,如果每次查询要涉及到最外轴上的一个区间。那么一是可以“令若干个轴地位平等”,使用莫队;二是用分块;三是坚持扫描线,做线段树历史和;四是用类猫树分治。如果性质特别好,还可以直接差分。除了莫队以外,这些办法都要求信息可合并
特殊情况,如果痛恨删除操作,不妨把最外层线段树分治/回滚莫队;如果痛恨修改操作,不妨最外层分块并定期重构(有时应将修改视作先删除后加入,如果修改不比较复杂,考虑如何将一个大修改分成若干“先删除后加入”的“单位修改”)。
特殊情况,如果最外层是值域轴,可二分询问的答案,可以拆贡献到每个修改/元素上,贡献依任意顺序可加。那就可以整体二分最外轴。
特殊情况,如果可以拆贡献到每个修改/元素上,一切贡献从最外层的小端向大端进行。那就可以 cdq 分治最外轴。
特殊情况,最外轴翻转,加入变删除,删除变加入。
特殊情况,元素具备若干种形态。而形态之间的转换是不可逆的。暴力维护元素在形态之间的转变即可。这就是珂朵莉树。通常还会把同种形态的元素连续段缩起来。
特殊情况,具备若干种不同角度的暴力做法,可平衡复杂度。
特殊情况,问题涉及两个元素之间的关系,但是直接维护“任两个元素间的关系”是不可取的。可用根号分治把关系变成单向(从小点到大点)的。如果某些元素的和是一个不大的定值(\(10^12,10^5\) 分别代表 \(O(\sqrt n),O(n\sqrt n)\) 的复杂度),也不妨根号分治。千万注意在这种情况下,有两种和根号有关的对称结论。第一个是“大于 \(\sqrt n\) 的元素仅 \(O(\sqrt n)\) 个”,第二个是“所有元素只有 \(O(\sqrt n)\) 种本质不同的出现次数”。如果想到了其中一个,一定要提醒自己,另一个结论此时也成立。

上述所有东西都能用来优化其它算法,尤其是优化 dp。

T-102-1 P11364

前面都比较容易,把问题转化成:有 \(O(n)\) 个区间 \([l,r]\),每个区间有一个权值。\(O(n)\) 次询问,与 \([q_l,q_r]\) 交集大于等于 \(q_k\) 的,权值最大的区间的权值。如果把“区间长度”也当作一个维度,就相当于常规三维问题,树套树做到 \(O(n\log^2 n)\)。但是期望 \(O(n\log n)\) 求解。

把区间当成点 \((l,r)\) 画到二维平面上,每次相当于查询以下区域的交集内点的最大权值:\(y\) 轴右侧;某条 \(y\) 轴右侧平行于 \(y\) 轴的直线左侧;一条 \(x\) 轴上方平行于 \(x\) 轴的直线上方;某条平行于 \(y=x\) 的直线左上方。信息满足可按任意顺序合并性、可重算性,但不满足可减性。

要用一只老哥解决问题,看一只老哥能做什么。本问题是可离线的,而且看起来用不到真正的矩形,用半矩形就够了。怎么用半矩形处理呢?因为可以重算,但不能漏算,且不能差分,可以先随便上几个半矩形,只要不出现“超出界限,导致必须差分”的情况就行,然后对着没有算到的地方继续想。原有图形是一个五边形。很自然地想到,从五边形右下侧的两个顶点,各自引一个“以该顶点为右下角”的矩形(关注对称性,所以引了两个矩形)。此时未被算到的部分其实只有一个等腰三角形,不太好处理。因为能重算,只需找出一种办法,在不覆盖原五边形以外区域的条件下,完全覆盖该等腰三角形即可。

法一,等腰三角形的难点在于腰是斜的。不过别忘了,斜的矩形也能扫描线,旋转一下坐标轴就行了。因此以“腰”为一条边、过“腰”两端点且垂直于腰的两条直线为另两条边,确定的半矩形,就满足条件。

法二,尝试优化 \(O(n\log^2 n)\) 的做法。不妨让难处理的“腰”做最外层。具体来说,依 \(q_k\)\(r-l+1\) 联合排序,顺序扫描,依次加点。那就无需考虑“腰”的条件,因为“腰”所在直线以下的点都尚未加入平面。换句话说,此时在“腰”所在直线以下都是空白,允许你所划分出的图形包含任意多的空白。现已是“在线问题”,想想如何运用“两条平行直线之间的最大值”解题。显然,取“腰”两端点的横坐标 \(x_1,x_2\),直线 \(x=x_1,x=x_2\) 之间的部分就满足条件。

像这种区间转换成二维点的问题,需千万注意的一点是,无论何时直线 \(y=x\) 下方都是空白。这个法二还启示我们,可以把图形分成若干部分,一部分离线求,另一部分在线求。

T-102-2 P14638

本题的法一和法二已经在 T-001~T-100 中介绍过,这里讲法三。这个法三呢,可以给到 S+,甚至能进 Ultra Examples,因为实在太难想到了。

每个询问相互独立,且“异或”仅仅是为了加速输出。那对一次询问,问题就是对所有 \(i\),求:直线 \(y=x+R,y=x+L\) 与直线 \(y=i,x=i\) 交得的等腰梯形内点的点权最值。不过,点有 \(O(n^2)\) 个,恐怕得一次性算好多点。同一批次算的点间,关系匪浅。我一再强调这种“关系”,通常是相邻,进一步可能是连通块的关系等。假设是一维关系,也就是查询“某线段上点的点权最值”,其时间复杂度 \(O(\log)\);若是二维关系,也就是查询“某矩形内点的点权最值”,还是 \(O(\log)\)(用 st 表预处理可达到 \(O(1)\))。本题中的信息满足可按任意顺序合并性、可重算性,但不满足可减性。期望复杂度 \(O(nq)\)

对于一次询问,需要快速处理若干本质相同问题的答案,用递推。一般是用关系相近的两个状态做递推,不妨尝试用 \(i\) 的答案推向 \(i+1\) 的答案。此时,需要加入一竖溜、删除一横溜。但没有可减性,故无法“删除”。那就是说,得改变递推的基础状态,使得递推过程中不需要“删除”。同时,用 \(O(1)\) 个基础状态能够拼出(可重复)任意一个所求状态。既然“加入一竖溜/横溜”好做,想想,有什么基础状态能够用这个玩意递推,不妨令基础状态为“平行四边形”。具体来说,令直线 \(x=i,y=x+L\) 的交点为 \(P\),过 \(P\) 做平行于 \(x\) 轴的直线,交 \(y=x+R\) 于点 \(Q\),则由 \(PQ,y=x+L,y=x+R,y=i\) 四条直线构成的平行四边形就是基础状态一。同理,做对称可得到 \(P'Q'\) 和另一个平行四边形,称为基础状态二。一、二可以分别递推。

可是,用一、二不一定能拼出所求状态。事实上,当 \(\frac{R}{L}>2\) 时,直线 \(PQ,P'Q'\) 有一交点 \(T\),做过 \(T\) 关于 \(x,y\) 轴的平行线,交 \(y=x+R\) 于两点,则此三点构成的等腰直角三角形未被算到。因此需要对它也进行割补,增加“基础状态”。新的基础状态呢,可以递推去求,也可以 \(O(1)\) 算出,不准算多,可以算重(可与一、二有所重复)。比较容易想到,因为 \(T\) 也在一条定直线上,只需将这条定直线,\(y=x+R,y=i,x=i\) 四条直线构成的等腰梯形内的部分算出即可,自递归成子问题,总复杂度 \(O(nq\log n)\)

但是无法通过,其核心在于我们把一个“等腰直角三角形”放缩成了“等腰梯形”,放缩过火了。要利用好这个等腰三角形的性质。三角形是一条边退化的梯形,这启示我们利用好直角顶点。我刚才提到,可以 \(O(1)\) 算出基础状态,而矩形就能 \(O(1)\) 算出,不妨以直角顶点为右下角做三角形的尽量大、尽量对称的内接矩形,也就是一个内接正方形,称为基础状态三。这样就只剩下两个小三角形部分没有覆盖到,再用两个平行四边形(基础状态四、五)即可处理。

综上,状态一、二、四和五,可各自递推求解;状态三 \(O(1)\) 直接算;用这五个状态 \(O(1)\) 拼出所求状态,时间复杂度 \(O(nq)\)。那么这道题启示我们,当元素过多时,考虑把若干元素合并成一个二级结构统一计算,二级结构内的元素关系匪浅。而每次查询只准调用二级结构外加 \(O(1)\) 个单点元素,这其实就是分块的思想来源。另外,正如 T-101-1 所说,把所求图形划分成若干基础状态之后,各个基础状态的求法,可以不同:状态一离线求,状态二在线求,状态三彼此递推求,这都可以。

T-102-3 CF1830F

把区间画在二维平面上。本题中有个“解”字,尝试描述法,描述法的第一要务是,算答案方便;第二要务是,与原题面中所有可能性一一对应/多一对应,在最优化问题中,至少要和“优秀/合法”的那些可能性对应上。这样,就有两个明显的描述法。一个是以“算答案”为重点,记录每个区间被哪个激活点贡献。不过这不是直接描述原题面的可能性,故需要判定你所描述的东西,有没有一个原题面的可能性与之对应,不太好搞;第二个是以“对应”为重点,直接描述原题面的可能性,也即每个点是否被激活,这就要关心答案如何计算。在二维平面上,把被激活点 \((i,i)\) 左上方的半正方形涂色,称为“\(i\) 贡献正方形”。则以被激活点 \(i\) 为主体,它所实际贡献到的区间,是所有,属于“\(i\) 贡献正方形”,但不属于“第一个大于 \(i\) 的被激活点 \(j\) 的贡献正方形”,的点。这些点也构成一个半矩形。

依据此描述法做 dp,显然有一个断点转移\(O(n^2)\) dp。状态为 \(O(n)\),应该做不了手脚了;是半在线形式。显然不能 cdq,那能不能决策单调性?不可能,因为 \(p_i,i\) 不是同时单调的。这些都不太行,不妨转逐步转移。也就是说,改为定义状态 \(f_{i,j}\) 表示,现位于二维平面上 \(y=i\) 这一行,上一个被激活点的横轴坐标为 \(j\),只考虑 \(y\le i\) 的那些区间,其权值和最大是多少,转移当然是 \(O(1)\) 的。此时你看这个状态就爆了,把用于优化状态的套路弄进去看看。好像**整体 dp **就非常有前途。数据结构上去看,这相当于在线 \(O(n)\) 次区间修改 \(c_i\),并查询 \(\max a_ic_i\)。这是分块建凸包,或者 KTT 的板子,即可 \(O(n\sqrt n)\)\(O(n\log^2 n)\) 解决。

这个题本身倒不算难。但是分块建凸包或 KTT 是比较著名的算法。而且,断点转逐步,相当于用大状态代替大转移,然后再去优化那个大状态,这就把对转移的优化改成了对状态的优化,还是很常见的技巧。可以用这题稍微复习一下优化 dp。

不过最重要的在这里。逐步转移的 dp,有时候对应的描述法是跟原来不一样了。在本题中,不妨逆向分析一下逐步转移 dp 到底“是在 d 什么”。它实际上描述了某条从 \((0,0)\) 开始的折线,这个折线相当于所有“贡献正方形”的外边界。显然,描述这条折线就足够计算答案,故该描述法也正确。

T-102-4

给定一长度为 \(n\le 2\times 10^5\) 的序列 \(a\),以及一常数 \(k\)。现有 \(k\) 次查询,每次问,在区间 \([l_1,r_1]\) 中选择一个点 \(i\),并在 \([l_2,r_2]\) 中选择另一个点 \(j\),要求 \(a_i+a_j\le k\),在此条件下 \(a_i+a_j\) 的最大值是多少。同时问能够达到该最大值的有序数对 \((i,j)\) 有多少对,注意 \([l_1,r_1],[l_2,r_2]\) 可能有交。强制在线。

在二维平面上画图,将数对 \((i,j)\) 画到平面对应点上,则每次相当于查询一个矩形内的信息。这信息还是个二元信息,它显然支持以任意顺序合并,但不可减,也不可重算。数据范围提示根号,又要在线,而且 \(k\) 恒定也提醒了预处理,不妨分块。在此处插入本博客有史以来第一张图

T-102-4

黑格线代表块缝隙,红格线代表一次查询的矩形范围,本图例中有三次查询。把问题分割为整块对整块(绿色)、散块对整块(蓝色)、散块对散块(黄色)这三部分。你注意,在这类二维平面上的分块题目中,“基础状态”是一个“整块对整块”“散块对整块”或“散块对散块”。举个例子,本图中最下方的询问就涉及到了十二个“整块对整块”。不过基础状态分为了三大类,用不同的颜色表示出来,同一类的状态很可能要统一处理。

先看黄色部分。千万注意,在每次查询时我们还有 \(O(\sqrt n)\) 的时间可以花费。一次询问只有 \(O(1)\) 个黄矩形,对一个黄矩形,不妨一列一列算答案并拼起来。具体来说,一个黄矩形,需 \(O(\sqrt n)\) 次”小查询”,“选择 \(i\),并在 \([l,r]\) 中选择 \(j\)”的信息,这几次查询的 \([l,r]\) 都一样。注意,因为图中最上面、中间的两个询问给出了例子,可知 \([l,r]\) 是满足 \(l,r\) 属于同一块内的任意区间,不一定满足“\(l\)\(r\) 与块端点重合”这一性质(预处理某块的前后缀信息,在分块问题中比较常见,可惜此处不满足这一点)。对黄矩形预处理 \([l,r]\) 内元素的顺序,则每个“小查询”只需问“某位置的前驱元素”,显然可以二分,不过可以用六十四叉 trie 或者类链表并查集来做到近似大常数 \(O(1)\),但这不好。不妨将每个块预排序后存到若干数组中,每次查询时把黄矩形涉及到的元素拉出来归并即可。

再看绿色部分。恐怕需要把“在第 \(i\) 块选一个元素,并在第 \(j\) 块选一个元素,的信息”预处理出来一个 \(\sqrt n\times \sqrt n\) 的数组(用归并的办法,复杂度可以接受),查询时,每次只需在线问一个矩形内的值,可以接受 \(O(\sqrt n)\),这显然要用某个数据结构去处理,并且该数据结构的预处理复杂度最多 \(O(n\sqrt n)\)。不妨用 kdt 或者四分树完成。

最后看蓝色部分。由图中最上面那个查询的例子可以知道,蓝色部分也不一定是块内的前后缀等。预处理“选择元素 \(i\),并在第 \(j\) 块选择另一元素的信息”,也是一个 \(\sqrt n\times \sqrt n\) 的数组,查询时还是用 kdt 或四分树。

总结来说,如果贡献是类似 pair 型的问题。那么可以序列分块,然后预处理块与块间、单点和一个块的答案。画出上述图示,辅助分析。

T-102-5 为什么我不会做这个题,才刚刚总结过类似的东西啊?!

\(n\le 2\times 10^5\) 个盒子,第 \(i\) 个盒子一开始装有 \(a_i\) 个红色糖果和 \(b_i\) 个蓝色糖果。它的容量为 \(c_i\),且保证 \(c_i\) 为偶数。其中最后一个盒子(第 \(n\) 个盒子)例外,它没有容量限制。现打算进行一些糖果添加操作。对第 \(i\) 个盒子加入一个糖果时,会发生以下情况:如果加入糖果后,第 \(i\) 个盒子中的糖果总数不超过 \(c_i\),或者该盒子是第 \(n\) 个盒子,那么糖果将直接加入其中。否则,此时盒中的糖果数必定为 \(c_i+1\) 个。此时会从数量较多的那种糖果中取出一个“拿在手上”,将其从当前盒子中移除,再放入第 \(i+1\) 个盒子。对于第 \(i+1\) 个盒子,将重复上述过程(即递归处理)。需要支持 \(q\le 2\times 10^5\) 个操作,操作分为以下三种类型:在第 \(x\) 个盒子中加入 \(v\) 个红色糖果;在第 \(x\) 个盒子中加入 \(v\) 个蓝色糖果;查询当前时刻,第 \(x\) 个盒子中红色糖果和蓝色糖果的个数。在添加糖果时,实际上是一个一个依次加入,并执行上述的传递过程。有一档重要的部分分是,初始时 \(a_i+b_i=c_i\) 均成立。

这个题可以与 T-097-5 对比着来看。题目分四个轴,分别是时间、位置(盒子的)、糖果个数。那么哪个轴放外面好呢?注意,时间是从小到大“转移”的,也就是说当前时刻的状态严格依赖以前时刻的状态;位置是从小到大“转移”的;糖果个数比较复杂。再看看 T-097-5,这道题目也是有三个轴,其中一个比较复杂,另外两个都具备“转移”的偏序关系。这种具备“偏序关系”的轴适合放外面。因为所谓“放外面”大概率就是去扫描它。

法一,把时间轴放外面,其实就是在线模拟整个过程。先思考暴力,期望得到一个与“糖果个数”无关的暴力。当修改时直接一路模拟到 \(n\),实时维护每个盒子里的糖果个数,以及打算给下一个盒子加入的糖果(称为“拿在手上”的糖果,这些糖果之间有“添加时序”的顺序)。但这不容易,因为大概率,在单次修改中,模拟到一半时,“拿在手上”的糖果,已经依添加时序分成了好多连续段,一段蓝一段红的。如果你非要维护这些段,就很不可做。不妨猜想,对于同一个盒子给它加入若干糖果,盒子的终止状态,也即盒子“往外吐”的糖果种类和个数,跟糖果加入的顺序无关。这结论是正确的。

就容易得到一个 \(O(nq)\) 的暴力。以一个盒子为主体,类比 T-052,从函数的角度,你会发现它可分为三个状态。一个是“没有放满”,第二个是“放满但没有平衡”,最后一个是“平衡”。我真的真的奉劝大家,不要看到分段函数,就上分块维护分段函数。一定一定一定要仿照 T-052 的思路,把元素分成若干类,维护元素在两类之间的转化,显然一个元素只会转化 \(O(1)\) 次。那借鉴这个思路,在模拟的过程中实时把“没有放满”的盒子红色标出,把“未平衡”的盒子黄色标出,那似乎只需快速维护,当前“拿在手上”的糖果经过无色连通块后的结果(其实就是没受影响)即可。因为一旦遇到被标出的元素,该元素一定要转换状态。

但这不是真话。仔细分析,若碰到红色元素,要么它转换状态,要么你手上的糖果都被该盒子吃了,直接结束模拟,这没问题;但若碰到黄色元素,有可能你手上的某色糖果不够多,使得该黄色元素未被填满,反而是手上的该色糖果均被转化。然而即使手上全是一种颜色的糖果,也并不意味着模拟应该结束。所以有可能需要遍历很多黄色元素,这些元素还不会被转换状态,就不能暴力一个一个遍历了。我所好奇的应该是“下一个被转换状态的黄色元素”,在它之前、当前位置之后的这一段,只需对中间的黄色元素打个 tag 就行了吧?这个做法比较难写,然而允许强制在线。

法二,把位置轴放外面,从小到大扫描位置,只需维护“从时刻零到正无穷,当前位置盒子内的状况”,和维护“从时刻零到正无穷,经过当前位置后手上糖果的状况”是完全对称的。用一棵线段树维护“手上糖果的状态”,每次在树上二分找到时间段,然后进行对应的区间赋值操作(多整几个 tag 例如“被某盒子吃了”“全被改成蓝糖果”“全被改成红糖果”等)即可。非常的好写(相对而言)。

T-102-6 P14719

法一。枚举 \(t\) 的长度 \(i\),只需贪心扫一遍就能知道长度为 \(i\) 时最多替换多少次,这是一个 \(O(n^2)\) 的办法。带哈希表的常数,可以改成 sa 去掉那个常数。接下来有两种办法去理解下一步。

第一种,设上述答案为 \(f(i)\),相当于求若干本质相同的问题。没有办法改变主体,更不可能递推,故只可能统一计算。如何统一计算呢?先看我们有哪些轴,一个是 \(i\) 这个轴,另一个是 \(f(i)\) 的值域轴。关注单调性/凸性,可知 \(i\) 大时 \(f(i)\) 就小。想想我们的目的,看连续性!\(i\) 轴上连续,也意味着 \(f(i)\) 上同时连续,能不能够把连续段一次性求呢?求一个连续段最少 \(O(1)\),这要求连续段数不能太多,看一眼数据范围,好像根号量级是能够接受的。用根号分治证一下,当 \(i>\sqrt n\)\(f(i)\le \sqrt n\),这意味着 \(f\) 确实只有 \(O(\sqrt n)\) 种取值。先求出包含 \(i=1\) 的连续段,不断地用二分求出当前连续段的末端并跳过去即可,容易做到 \(O(n\sqrt n\log n)\)。这种多次二分的问题,可以用分治优化掉老哥。就是类似于决策单调性分治/整体二分的过程,一旦发现当前分治区间对应的值域重合,就统一赋值并返回。可以用 \(O(n\sqrt n)\) 的复杂度求出所有 \(f\) 的值,再 \(O(n)\) 计算答案即可。

补充一下。这种东西类似于:线段树上,已知有 \(O(\sqrt n)\) 个叶子节点是特殊的,需要找出其位置;可以 \(O(1)\) 判断一个子树内是否存在特殊节点。采用势能线段树的办法,如果子树内有,就递归下去。则此复杂度一样是 \(O(n\sqrt n)\),例如 CF1870G。

第二种,尝试 dp,\(f_{i,j}\) 表示以 \(i\) 结尾的,长度为 \(j\) 的串,最多在 \(i\) 及以前,不交重合多少次。移时需要贪心,跟直接贪本质相同。注意到该 dp 满足单调性,用答案和状态互换的对称技巧,你会发现没什么用,因为两种办法的“状态域”都是 \(O(n)\) 的。现在已经有了两种办法,可以尝试把它们拼起来平衡复杂度,就能看出根号的结论,接下来就跟上面完全一样了。

法二,这是个字符串题呀!字符串题经常用 kmp 刻画 border,用 manacher 刻画回文,用 sa 去刻画子串相同的限制。先后缀排序,则从小到大扫描 \(i\) 的过程相当于关心 height 数组,做并查集合并。很容易启发式合并维护出同一并查集内,子串的开头位置们。可是算答案还是得贪心,这个贪心不允许选择的子串有交,怎么优化呢?不用优化,每次合并时暴力跳更新答案即可,是调和级数的,就 \(O(n\log^2 n)\) 做完了。

T-102-7 P11118

首先假设只询问 \(k=n\)。做暴力,可维护一个优先队列,里面放置每个无人机经过下一段所需的时间。每次弹出队首,并将队首无人机的新时间加入队列。复杂度 \(O(nm\log n)\)。想到要合并连续段,不妨将同一无人机连续入队的过程,称作“连续段”。关注 \(s\)差分数组,则每个前缀最大值才意味着一个连续段的开头。连续段只有 \(O(\sqrt m)\) 个,当然你需要给每个连续段赋个权值 \(c_i\) 表示其代表的原有“门”个数。现在复杂度改为单组询问 \(O(n\sqrt m\log n)\) 了。

答案去考虑,可以用无人机为主体,只需分别求出每个无人机被传送了多少次即可。原题也是若干本质相同问题,不妨递推,尽量利用已有的结果。每次加入编号为 \(i\) 的无人机,在仅考虑前 \(i-1\) 个无人机的“结果”上做调整。就是实时维护每个无人机分别被传送了多少次。用分块平衡,做到总复杂度 \(O(n\sqrt n)\)

T-102-8 P6109

其实是一个不存在时间轴的题目,只有两维,就是题目给出的二维平面。这两维的地位相同,随便选一个放在外面。但是直接扫描线完全无法处理。我刚才介绍过,因为查询涉及到了最外层上的区间,可以外层猫树分治。这样一来,内层就只需用线段树历史最值维护即可。

T-102-9 P9999

有两个轴,一个是询问轴,另一个是序列轴。另外有一个树的结构。注意它没有强制在线,把序列轴放外面比较好。也就是从小到大扫描序列轴,实时维护每个询问中涉及到的点的当前位置。当扫描到 \(q_l\) 时额外向所维护的数据结构中,插入一个代表当前询问的节点即可。那如何快速维护若干点的移动呢?容易看出,除了 \(a_t\) 到根链这一段的部分是向某个儿子行动,其它部分的都是向父亲行动。树上链问题,第一个是考虑链-子树对称,但此处不可行,故改用树链剖分,关键的就是点“跨链”的问题,用平衡树维护就行了。可以做到 \(O(n\log^2 n)\)

T-102-10 P6774

有两个维度,一个是位置轴,一个是值域轴。但本题是“点对”题目,一般而言要把位置轴拆成两维,如 T-102-4。本问题严格强于“区间逆序对”(寻找一个熟知的问题,并比较这两个问题的强弱,可以得到新问题的复杂度下界,也可借鉴其想法)必须带根号,所以大概率是要序列分块。在三维空间分析性质,每次类似于问一个长方体内部点个数,是满足可加减性的。

序列分块,也就是说把两维位置轴放在外面,而内部值域轴利用分块维护的信息处理。分讨一下,第一种 \(i,j\) 在同一散块内部;第二种 \(i,j\) 在同一整块内部;第三种 \(i,j\) 分属两个整块;第四种 \(i\) 属于散块 \(j\) 属于整块;第五种 \(i,j\) 分属两个不同的散块,依次解决。

每个块预离散化。我介绍过,查询时有 \(O(\sqrt n)\) 的“额外时间”。对第四、五种,用“额外时间”枚举 \(i\),剩下一个必须 \(O(1)\) 查询的二维数点。可首先利用可减性变成“半矩形”。在剩余二维上做扫描线,只不过要求“查询 \(O(1)\),修改 \(O(\sqrt n)\)”,把数据结构换成值域分块即可。当然第五种也可直接归并排序解决。第一种,枚举 \(i\) 之后,需询问块内满足 \(j\in[i,q_r],p_j\in[p_i,b]\)\(j\) 个数,预处理并差分。第二种,枚举所在块后必须 \(O(1)\) 查询,可预处理二维前缀和表示块内满足 \(p_i\in[1,a],p_j\in[1,b]\) 的合法 \((i,j)\) 对数,差分一手。第三种,查询时能够枚举 \(i\) 所在块是 \(B_p\),剩下的相当于 \(O(1)\)\(p_i\in[a,b],i\in B_p,j\in[p_i,b],j\in[B_{p+1},B_q]\) 的对数。块内依 \(p_i\) 排序后,预处理 \(f_{p,r,q}\) 表示满足 \(p_i\)\(B_p\) 块内前 \(r\) 大,且 \(j\in[p_i,b],j\in[B_{p+1},B_q]\)\((i,j)\) 对数。另外计算 \(j>b,j\le B_q\)\(j\) 个数用于差分,即可。

T-102-11 P7603

首先,一个数的质因子个数仅 \(O(1)\) 个。假设没有强制在线,则问题具备可二分性,且贡献可以拆到各个房子上,能够整体二分处理。就是说,维护已经确定答案值域(时刻)属于区间 \([l,r]\) 的询问分别是哪些,以及其“考虑完 \([1,l-1]\) 的闹鬼事件之后,还距离多少才到达阈值”;同时维护时刻属于 \([l,r]\) 的一切闹鬼事件。整体二分同一层所维护的询问个数与事件个数之和为 \(O(n)\)。对于区间 \([l,r]\),只考虑 \([l,\mathrm{mid}]\) 内一切事件对 \([l,r]\) 内一切询问的影响。如果该“影响”超出某询问的“距离”,则直接让该询问递归到下一层 \([l,\mathrm{mid}]\);否则递归到 \((\mathrm{mid},r]\) 并令其“距离”减去“影响”。

但是它强制在线了。有一个叫做“折半警报器”的东西,学一下。就是对于一个“监控器”(查询),在被它监控的位置各设一警报器,警报器的阈值是“设置警报器时,监控器‘距离’/监控位置个数”。一旦发现某警报器超过自己的阈值,就重新设置对应询问的所有警报器。这样一来,在警报器均不超出自己阈值的情况下,监控器本身也不可能超出阈值,故正确。复杂度 \(O(n\log^2 V)\)

T-102-12 P13130

本题有三个轴,一个是元素编号轴,一个是值域轴,一个是时间轴。注意到没有添加元素,所以不同种类的元素个数之和是 \(n\),考察根号分治的结论。那么有两个,一个是大小 \(>\sqrt n\) 的种类仅 \(O(\sqrt n)\) 个;第二个是,所有种类的大小共计 \(O(\sqrt n)\) 种。此处是选用了第二个结论,只需维护出“所询问的时刻和范围内,大小为 \(i\) 的种类个数 \(c_i\)”,则 \(c\) 数组仅 \(O(\sqrt n)\) 个元素有值,就能双指针回答询问了。

\(c\) 数组作为关键信息,不具备可快速合并性。经过分析,只好用莫队。这个莫队还得是三维(带修)莫队,“时间轴”的长度应该是“单位操作”(也就是启发式合并中的加入元素)的次数 \(O(n\log n)\),直接莫队带老哥,过不了。莫队是痛恨修改的,似乎可以用定期重构加上普通莫队,做到与带修莫队一致的复杂度,但没什么用。

在此介绍一个 trick,专门用于处理“修改是集合合并”的情形。对于待合并集合 \(x,y(|x|<|y|)\),合并结果存放在集合 \(y\)。给每个集合维护一个“缓冲区”,大小至多为 \(B\)。当 \(|y|<B\) 时不启用缓冲区直接重构;反之,合并时把被吃掉的集合元素先放在缓冲区。若发现某集合的缓冲区大小超限,则重构清空缓冲区,加入真正的集合。我们的莫队,可以看作针对每个集合维护一对指针,故一旦发现集合重构,就放弃对应莫队的一切信息,从头开始(类似于定期重构的间隔中,跑莫队)。每次查询时,额外加入一次“缓冲区内容”,又删掉一次。复杂度是什么呢?第一种是 \(|y|<B\),不调用缓冲区直接重构,单次这种合并的复杂度低于 \(O(B)\),如果查询到这种 \(|y|<B\) 集合时也不用莫队,暴力查就是对的;第二种若 \(B<|x|\) 则必然直接重构 \(y\),但这种合并不会超过 \(O(\frac{n}{B})\) 次;第三种是把较小的集合 \(x\) 加入了 \(y\),你注意任何一个 \(|x|<B\) 的集合的“归宿”不是第一种就是第三种,故由第三种加入缓冲区的次数至多 \(O(n)\) 次,造成的缓冲区重构仅 \(O(\frac{n}{B})\) 次。所以“修改-重构”造成的复杂度为 \(O(nB+\frac{n^2}{B})\)。莫队过程中每次查询要额外搞 \(O(B)\) 次;莫队移动指针的复杂度又是什么呢,假设把各个集合在相邻两次重构之间遇到的查询次数统一写出为 \(q_1,...,q_k,\sum q_i=q\)。则第 \(i\) 次莫队的复杂度为 \(O(\frac{n^2}{B_i}+q_iB_i)=O(n\sqrt{q_i})\),总 \(O(\sum n\sqrt{q_i})<O(\frac{n}{B}n\sqrt{q\frac{B}{n}})\),故总复杂度 \(O(nB+\frac{n^2}{B}+qB+\frac{n}{B}n\sqrt{q\frac{B}{n}})\)。当 \(B=n^{\frac{2}{3}}\) 时取得 \(O(n^{\frac{5}{3}}+n^{\frac{4}{3}}+qn^{\frac{2}{3}}+q^{\frac{1}{2}}n^{\frac{7}{6}})<O(q^{\frac{5}{3}})\),可以通过。

T-102-13 P11695

本题有时间轴、位置轴。称所询问的区间为“询问区间”,可重集合内的区间为“贡献区间”。先想 A 性质,因为没有任何修改,所以只存在位置轴。因为查询是区间的形式,先尝试传统扫描线,也就是扫描 \(r\) 并维护“若询问区间起点为 \(l\) 且终点大于等于 \(r\),则只使用 \([l,r]\) 内的贡献区间,能够让答案最远拓展到哪里”,这很难处理。如果用树套树,因为标记不可合并,恐怕做不了。若使用分块套树等,复杂度也难以接受。

改用猫树分治,对于一个询问,我们把它挂到某猫树节点上。传统的猫树分治只考虑“跨过当前猫树节点中点的‘贡献区间’对询问区间的影响”,这是不能处理的,因为这样的话,大概率询问区间两端有“留白”,这些留白难道要递归求解吗?显然不可能。所以说,对猫树的理解要更深一层,猫树分治允许考虑“与当前猫树节点有关的‘贡献区间’”,在本题中,就是指“被节点区间包含”。换句话说,不仅考虑第一部分“跨过当前猫树节点中点的‘贡献区间’”,还要用到第二部分“被当前节点某儿子节点包含的‘贡献区间’”,使得一个询问只访问一个节点就得到解决。

现在直接解决整个问题,也就是加入时间轴,把每个“贡献区间”写成 \((l,r,t_l,t_r)\) 的四维形式,把“询问区间”写成 \((q_l,q_r,q_t)\) 的三维形式。时间轴不应该放外面,故还是用猫树分治做最外层。则第一部分形如问“当前节点满足 \(t_l\le q_t\le t_r,l\in[q_l,\mathrm{mid}],r\in[\mathrm{mid},q_r]\) 的‘贡献区间’的 \(r\) 最大值是多少”,第二部分形如问“当前节点满足 \(t_l\le q_t\le t_r,r\le q_r\) 的‘贡献区间’的并中,包含 \(q_r\) 的连续段的左端点是多少”。

对第一部分,有“可以拆成两个”的时间轴(因为是区间 \([t_l,t_r]\) 形式)、位置轴 \(l\)、值域轴 \(r\)。直接上树套树能做到总复杂度 \(O(n\log^3 n)\)(因为猫树上共计挂了 \(O(n\log n)\) 个“贡献区间”),不能接受。这样做,则单个“贡献区间”时间复杂度 \(O(\log^3 n)\),而“询问区间”复杂度 \(O(\log^2 n)\),并不平衡,暗示有优化空间。尝试把时间轴放次外层。线段树分治一定没前途(复杂度一定不对),只能用扫描线。把值域轴放第三层,而 \(l\) 用维护的信息表示,查询时线段树二分即可。至于扫描时间轴过程中的修改与删除,不妨给每个 \(r\) 开一个 multiset 表示以它结尾的“贡献区间”之开头的可重集合,就能转化成若干次单点改。总复杂度 \(O(n\log^2 n)\)

对第二部分,有“可以拆成两个”的时间轴、位置轴 \(r\)、特别复杂只能放最内层的答案轴。把时间轴放次外层不太好搞,改为把位置轴放次外层做扫描线、时间轴放第三层,最内层答案应是二元信息表示“最后一个连续段是谁”。好就好在这二元信息支持合并,直接打标记就行了,总复杂度 \(O(n\log^2 n)\),我觉得是对的但没写,没有题解这样做的,那就是假了吧。另一个办法是翻转位置轴倒序做扫描线、时间轴放第三层,最内层答案应是一元信息表示“最后一个连续段的左端点”(因为是倒序做扫描线,所以“最后一个连续段”的右端点就是“编号”)。扫描线过程中若把有关 \(r\) 的修改操作都搞定了,就需要取出树中答案 \(>r\) 对应的若干元素,直接删掉(因为这些连续段在 \(r\) 处断开了)。

T-102-14 P11051

这是一个最优化问题,大致的方向就是 dp、贪心、调整法、描述法。经过尝试,虽然描述法/调整法能够得出一些结论,但作用有限,只能得到 \(O(n^2)\) 的 dp,虽能通过子任务二,但似乎没有优化空间。

先看第一个子任务,各个询问独立计算。我一再强调 dp 和贪心的区别是什么,dp 是把子问题中可能与全局最优解有关的解都记录下来;贪心是只把子问题的最优解记录下来(如果有多个最优解,只记录“对外界”最优的解)。当然,也不一定记录解本身,而是记录“‘外界’若要调用‘子问题的解’,所要用到的东西”。若在本子任务中贪心,那么唯一的想法就是记录“让子树 \(x\)(子问题)的代价尽量少时,子树的系数和是多少、代价是多少”。接下来考虑合并子问题(类树形 dp),先“接收”各子树的最优解,然后决策当前子树根节点的系数,做出调整。因为要求子树内代价少,所以根节点“非必要不操作”,换句话说若各子树最优解系数和超过 \(R\),才做调整,并只调整到 \(R\) 为止。当然,叶子节点应该被初始化为 \(L\)。这个贪心对于满足子任务一所述限制的树都正确。

对于子任务四,也满足上述限制可以贪心,但因有多组询问,所以必须优化。不妨更改贡献主体,想象每个叶子节点上放置了 \(L\) 个“小球”,小球不断往父亲方向滚。以小球为主体,若小球被删除时则给全局答案增加一,而对一个小球,它最多被删一次。小球之间没有差别,只好奇最后剩了几个小球。到最后,设共计 \(k\) 个叶子,则恰有 \(\min(kL,R)\) 个小球未被删除,换句话说答案就是 \(\max(0,kL-R)\)

子任务五,还能不能贪心了?假设可以,思考子问题答案的合并过程是否合理。只有两种节点,分类讨论。对一个“零”节点,从它子树上来的系数和无论是多少,都能不费代价地调整,贪心地调整到 \(L\)(“对外界”最优)。对于“一”节点,就跟上面一样地做,这样一来贪心仍然正确。加速这个贪心,仍旧想象有一些小球。注意“零”节点所删除的小球不计代价,故只好奇有多少小球被“一”删除。这不好算,不妨做对称,只需求出多少保留到最后、多少被“零”节点删除。第一部分好算,而第二部分只需枚举“零”节点的儿子,算出有多少小球到该节点即可。为了算这个,需要向下拓展连通块直到碰到“零”节点。总结一下,只需把“零”节点当作叶子挂在“一”节点连通块的边缘,然后对每个连通块都求子任务四的答案即可。答案应该是 \(\sum \max(0,a_iL-R)\),拆 max,需要判断 \(a_i,\frac{R}{L}\) 的关系,按照 \(\frac{R}{L}\) 将询问排序,然后扫描一遍即可。

子任务五明示了,整道题目应该要扫描值域轴 \(x\),将小于等于 \(x\) 的记作零、大于等于 \(x\) 的记作一,然后做若干遍子任务五并把答案加起来。直接做是 \(O(nq)\) 的(先离散化)。改为以连通块为主体,因为扫描 \(x\) 的整个过程中合计仅 \(O(n)\) 个连通块,所以能够接受。具体就是翻转值域轴,从大到小遍历 \(x\),每次合并若干连通块,即可。

posted @ 2026-03-16 09:08  timefinder  阅读(2)  评论(0)    收藏  举报