Exemplary Examples III

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

导语见 Exemplary Examples I。

T-081 P9120 25-11-24 B

先用二分极差,之后枚举每一行的最大值,转化成判定性问题,总复杂度 \(O(n^{k+1}\log V)\)。现想要减少枚举量。从边界元素入手,关注全局最大值元素,它一定是某一行的最大值。做对称,也可关注全局最小值元素。若此二者在同一行,则极差必为全局最大差,可特殊处理。若二者不在同一行,则可减少两行的枚举量,得到 \(O(n^{k-1}\log V)\) 的做法。

如果把该枚举的元素都枚举完了,就只剩判定性问题。问题的答案是 0/1,随着你二分和枚举的信息而改变。关注你所枚举的最后一行(我们想要消掉此处的复杂度),把答案看成关于最后一行最大值的函数(其它本来要用到的参数,如前几行的最大值,当作常量),这是一组本质相同问题,可以递推去求。具体来说,设现在二分的值为 \(m\),现在正在从小到大枚举最后一行的最大值 \(x\)(其余行的最值均已确定),则依据其余行已经确定的最值,可以知道每个锁圈各自的,转到最后一行的数字可能性集合。单个判定性问题相当于询问,值域区间 \([x-m,x]\) 是否将每个锁圈的集合中的元素,各自至少包含了一个。在 \(x\) 从小到大依次增长的过程中,可以直接维护“第 \(i\) 个锁圈的合法集合中,大于等于 \(x-m\) 的最小元素是多少”。每次查询全局最大值即可,总复杂度 \(O(n^{k-2}\log V)\)

只剩 \(k=4\) 没有解决,关键在于我们枚举了两行。把判定性答案看成关于倒数第二行最大值的函数(把二分的值和前面两行的最值当作常量。如果存在一个合适的最后一行取值,则答案就是 1,否则是 0)。不妨设倒数第二行最大值是 \(y\),则 \(y\) 从小到大增大的过程中,锁圈集合总计变动 \(O(n)\) 次。我想询问,在此 \(y\) 条件下(条件中还有被视作常量的,二分的值和前面两行的最值),是否存在一个合适的最后一行取值。以提出限制的元素为主体,从反面考虑。对于锁圈 \(i\) 集合,将它从小到大排序,关注其相邻元素 \(A,B\),则“最后一行取值”不能落在 \([A,B-m)\) 范围内。用线段树叶子节点维护“最后一行取值”能否位于当前值(将“不能被落在”的位置用区间加一去覆盖,则所有零元素就是可以被落在的位置),每次锁圈集合变动,只需区间加/减一即可。对于一个 \(y\) 的询问,就是问全局有没有零元素,若有,则存在合适的最后一行取值。总复杂度 \(O(n^{k-3}\log^2 V)\)

T-082 QOJ11318 25-11-27 B+

要求被操作的前缀单调递增,那描述法就简单了。一个方案可以由“操作位置(每次操作的前缀的末尾位置)组成的非重集合”唯一表示。但知道了方案,不代表知道最终答案(也就是由这个方案得到的串),先想想如何方便地由方案得到答案串(与之对称地,有时我们是去描述答案,然后考虑如何由这个答案,反推回方案)。因为每次操作都是对元素的位置变换,可以分拆成关注每个元素,也就是关心,初始第 \(i\) 个字符,最终在哪里。先关注边界元素,也就是那些位于最后一次、倒数第二次操作位置中间的元素。称这段元素初始时构成的子串为 \(S\)。则其最终状态是,\(S^T\) 被放置在总序列的开头。再关注次边界元素,也就是位于倒数第二、三次之间的元素,其最终状态是,直接被放在总序列末尾。换句话说,假设用所有的 \(k\) 个操作位置(为了描述方便,不妨设 \(k\) 为偶数)把原串划分成 \(k+1\)\(S_1S_2S_3...S_kS_{k+1}\)(合起来就是原串),则结果串是 \(S_k^TS_{k-2}^T...S_4^TS_2^TS_1S_3...S_{k-3}S_{k-1}S_{k+1}\)

因为“边界元素、次边界元素”等,都是从原序列靠左的位置依次剥离出来的。自然地想到倒序考虑,每次确定“倒数第 \(i\) 个”操作位置。相当于每决策出倒数第 \(i\) 个位置,就依奇偶性分讨,把倒数第 \(i,i+1\) 个位置之间的元素,要么翻转后分配给答案串最靠近开头的一段空白,要么直接分配给答案串最靠近结尾的一段空白(假设答案串初始全是空白。无论哪次分配后,答案串都是只被确定了一段前缀和一段后缀)。但这样做没有前途,其失败的关键在于,因为开头和结尾是分开的,所以在逐次分配的过程中,我们对“答案串”的字典序完全没有把握(只把握了前一半),也就不能了解,怎样的决策比较优秀。

对称,改成依原序考虑,则相当于每次把当前段翻转后,接到“当前答案串”前面,或者直接接到“当前答案串”的后面(“当前答案串”初始为空串。无论哪次分配后,“当前答案串”都是一个连续的串,且是最终答案串的一个子串)。但还是不会做。关键在于,现在这个描述法是接近于断点转移的“每次确定一段”,想要在不同方案之间进行字典序比较,是困难的。

换成逐步转移,也就是更改描述法。改为描述一个 01 序列,如果是 0 代表本元素属于某个“接前面”的段,是 1 代表本元素属于“接后面”的段。假设没有“不能操作某些位置”的限制,则我们可以依次决定每个元素是 0 还是 1,也就是决定它是要放到当前答案串的前面还是后面。每次在可能的两种串中,选择放置后字典序较小的串,作为本元素的决策结果。如果有限制,只需将连续有限制的段作为整体,每次确定一个整体是全 0 还是全 1,还是选择字典序较小的方式即可。多次比较两个串的字典序大小,可以二分其 lcp(用哈希判相同),然后判断下一个元素谁大。

T-083 P14638 25-12-8 A 颓了一周,因为 NOIP 考寄了。但是我们先别急,NOIP 的题目其实依然落入了窠臼。

题面的主体,是所枚举的 \(i\)。首先转换主体,就是说对于一次询问,只需考察所有长度在 \([L_j,R_j]\) 范围内的区间,对每个点 \(i\) 处答案的贡献。接下来,因为想要考察“所有”区间,可以分治

对于分治到的某个子区间 \([l,r]\),记其中点为 \(m\)。则在这个子区间处,只考虑所有跨过 \(m\) 且长度在 \([L_j,R_j]\),同时左右端点均在 \([l,r]\) 的区间,对于点 \(i\in [l,r]\) 的贡献。不好直接处理,不妨把贡献主体转换回去,对于点 \(i\),能够贡献它的区间只需多满足一条,就是要跨过 \(i\)。形式化地说,若区间 \([a,b]\) 贡献 \(i\),则 \(a\le i\le b\)。但是不要忘了,\([a,b]\) 必须是合法区间,换句话说 \(l\le a\le m,m\le b\le r\)。没想法吗?我再给你写慢一点,把所有限制叠在一起就是,\(a\in[l,\min(m,i)],b\in[\max(m,i),r]\)。这里存在两项取 min/max,因此需要根据两项的大小分类讨论。就是讨论 \(i,m\) 的大小关系。若 \(i\le m\),则限制就是 \(a\in[l,i],b\in[m,r]\)。把 \(i\in[l,m]\) 对应的可贡献 \([a,b]\) 集合,记作函数 \(f(i)\),则显然 \(f(i)\) 可以递推计算,\(f(i)=f(i-1)+([a,b]|a=i,b\in[m,r])\)。若 \(i\ge m\) 也类似。通过 st 表预处理,可以得到总复杂度 \(O(nq\log n)\) 的做法。

看特殊性质 D,因为 \(L_j>n/2\),只需访问分治区间 \([1,n]\) 而不用递归下去,因为两个子分治区间 \([1,\frac{n}{2}],[\frac{n}{2},n]\) 内均不存在合法贡献区间。性质 E 也类似。正常做分治过程,不过一旦发现当前分治区间 \([l,r]\) 满足 \(r-l+1<L_j\),就直接 return 即可。分治做法的前途到这里结束了,即使是继续优化分治做法,也必须要引入和分治无关的思想。这个思想就是:

定长分块。对原序列上,每隔 \(B\) 标记一个点,则任何长度 \(\le B\) 的区间,都最多跨过一个标记点。在本题中,每隔 \(R_j\) 放置一个标记点,把标记点 \(i\) 记作 \(m_i\)。那么,所有合法区间,要么恰跨过一个标记点(I 类),要么不跨过任何标记点(II 类)。关注 I 类区间的贡献,这个和分治时处理的问题完全相同,就不提了。关注 II 类区间的贡献,这些区间的端点必然均在某相邻两个标记点之间。很自然地,把端点在 \([m_t,m_{t+1}]\) 的合法区间放在一起考虑,称为集合 \(s(t)\)。又把贡献主体转换回去,只关心 \(i\in[m_t,m_{t+1}]\) 受到的,来自 \(s(t)\) 的贡献,不妨设对于 \(i\) 来说,能贡献它的集合是 \(g(i)\)\(g(i)=([a,b]|a\in[m_t,i],b\in[i,m_{t+1}],b-a+1\in[L_j,R_j])\)。最后一个限制的上界其实不用管。最奇怪的限制是最后一个,因为它是个双元限制

\(g(i)\) 写成 \(a,b\) 各自取值范围的形式。那双元限制如何处理,就看你把谁当作自变量。把 \(a\) 当作自变量,则 \(g(i)=([a,b]|a\in[m_t,i],b\in[\max(i,a+L_j-1),m_{t+1}])\)。把 max 的两项分讨一下,若 \(i\le a+L_j-1\),记作 \(h(i)=([a,b]|a\in[i-L_j+1,i],b\in[a+L_j-1,m_{t+1}])\)。反之,记作 \(w(i)=([a,b]|a\in[m_t,i-L_j],b\in[i,m_{t+1}])\)\(g(i)=h(i)+w(i)\)。对于 \(h(i)\),可以单调队列递推处理。对于 \(w(i)\),直接递推 \(w(i)=w(i-1)+([a,b]|a=i-L_j,b\in[i,m_{t+1}])\) 即可。

T-084 P14637 25-12-8 S

拿到这个题,先猜想它是 dp 去处理的。注意深度较小,那有两种可能,一种是有一维状态和深度有关,可能需要长剖优化转移。第二种是,状态/转移利用到了 \(\sum \mathrm{siz}_i=\sum d_i\le nd_{\max}\)。那么选择怎样的描述法呢?

以节点为主体,记录所有的 \(a_i\) 显然是一种描述法。但是我一再强调,要为答案服务,这个描述法不太容易计算答案。于是转换主体。最方便算答案的主体就是子树(毕竟答案就是每个子树的 mex 加起来),对应的描述法就是依次记录每个子树的 mex。对于 \(\mathrm{sub}_i\),记录其 mex 为 \(b_i\)。关键是某些 \(b_i\) 序列不合法。不妨假设知道某个 \(b\) 序列,用 dp 判定其合法性。

定义状态 \(f_i\) 表示,为了让 \(i\) 子树合法,我们定下了一些 \(a_i\),最多剩下几个节点未定 \(a_i\)。则 \(f_i=-(b_i-\max b_j)+1+\sum f_j,j\in \mathrm{son}_i\)。只需判断 \(\min f_i\) 和 0 的关系即可。

现在尝试 dp 逐步生成 \(b_i\)。状态 \(f_{i,j,k}\) 表示,生成了 \(i\) 子树内的所有 \(b_t\),其中 \(b_i=j\),最多剩下了 \(k\) 个节点未定,在此情况下,子树内 \(\sum b_t\) 最大是多少。前缀和优化转移,做到总复杂度 \(O(n^3)\)。但是没有前途。

换回主体,以 \(i\) 为主体如果想要统计答案,那就得知道,它的 \(a_i\) 给哪些子树的 mex 做了贡献。关键是如何定义“做了贡献”。从子树为主体考虑这个定义该怎么下。对于子树 \(\mathrm{sub}_x\) 内,\(\forall w\le b_x\) 都得定一个“贡献来源”,得到一个长为 \(b_x\) 的数组 \(g(x)\)。回想 \(b_x\) 的生成过程,它相当于在 \(x\) 儿子中,选择了 \(b\) 最大的 \(y\)“继承”了其 \(b_y\),并在此基础上,从子树内选取了一些未定节点,将其 \(a\) 依次赋值为 \(b_y+1,b_y+2,...,b_x\)。既然如此,不妨令 \(g(x)\) 的前 \(b_y\) 项就是 \(g(y)\),而 \(g(x)\)\(b_y+1,b_y+2,...,b_x\) 项,则依次是,刚刚在子树内赋 \(a\) 等于该值的位置。由此能够定义“贡献来源”,从而知道节点对哪些子树有贡献。

如此看来,这个“继承”关系形成的树链剖分结构非常重要,不妨描述每个节点的重儿子是谁。假设已知这个辅助结构,再去考虑每个节点能够对谁贡献、如何 dp/贪心最优化这个贡献。那么一个节点 \(i\) 所贡献的子树的根,一定是从 \(i\) 到根的这条链(称作 \(l\))上的某个“区间”。答案就是每个节点对应区间长度之和。对此区间的起点(深度较大端点)没有限制,不过其终点必须是,自起点开始向上数,\(l\) 上第一个满足“\(x\) 的父亲的重儿子不是 \(x\)”的位置 \(x\)。用调整法可得,若只关心 \(l\) 上的所有重边,则对于节点 \(i\) 来说,它的“区间”必然是贪心地选择,重边连成的链中最长的一条。据此,只要已知树剖结构,就能贪心算出答案了。

于是从上向下 dp,生成树剖结构。状态 \(f_{i,j,k}\) 表示,关心 \(i\) 的到根链,其中重边链中最长的一条长度为 \(j\),且深度最大的重边链长度为 \(k\),在此情况下,\(i\) 子树内所有节点贡献之和最大是多少。转移非常容易,只需枚举当前节点的重儿子是谁,可以做到 \(O(nm^2)\)。但是状态炸掉了,而且无法长剖优化。

断点转移。也就是说,生成树剖结构的时候,不要依次确定当前点的重儿子,而是一次性确定当前点一直到叶子的重链。仔细分析一下,可以定义状态为 \(f_{i,j}\) 表示 \(i\) 和父亲的连边是轻边,\(i\) 到根链上最长重链长度为 \(j\)\(i\) 子树内点贡献之和的最大值是多少。每次转移时,枚举子树内的一个叶子节点,将该叶子节点到 \(i\) 的路径设成重链,并将重链上点的其余儿子设成轻儿子。形式化地说,定义 \(\mathrm{path}(i,j)_{k}\) 为从 \(i\)\(j\) 的简单路径上第 \(k\) 个点。则

\[f_{i,j}=\max_{x\in \mathrm{leaf}_i}\sum_k(\max(j,k)+\sum_{y\in \mathrm{son}_{\mathrm{path}(i,x)_k},y\neq \mathrm{path}(i,x)_{k+1}}f_{y,\max(j,k)}) \]

分讨 max 两项谁大,把总和分成两部分。关键的节点是,到叶子重链上的第 \(j+1\) 个节点。链在此节点上方的部分,是 \(j\) 大;在此节点下面是 \(k\) 大。

\[f_{i,j}=\max_{x\in \mathrm{sub}_i,d_x=d_i+j}\left(\sum_k\left(j+\sum_{y\in \mathrm{son}_{\mathrm{path}(i,x)_k},y\neq \mathrm{path}(i,x)_{k+1}}f_{y,j}\right)+\max_{t\in\mathrm{leaf}_x}\sum_k\left(j+k+\sum_{y\in \mathrm{son}_{\mathrm{path}(x,t)_k},y\neq \mathrm{path}(x,t)_{k+1}}f_{y,j+k}\right)\right) \]

\(g_{x,j}\) 表示 \(\max_{t\in\mathrm{leaf}_x}\) 后面的东西。那么 \(g_{x,j}=\max_{y\in\mathrm{son}_x}(g_{y,j+1}+j+1+\sum_{z\in\mathrm{son}_x,z\neq y}f_{z,j+1})\) 可以 \(O(nm)\) 递推求出。剩余部分就是长剖,既然要长剖,就得把 dp 状态变成三维的“逐步转移”形态。可以改换主体,也就是枚举 \(x,j\) 后思考,它能够贡献哪些 \(f_{i,j}\)。然后下标换元使状态与深度有关。记录

\[h(j)_{i,a}=\max_{x\in \mathrm{sub}_i,d_x=a}\left(\sum_k\left(j+\sum_{y\in \mathrm{son}_{\mathrm{path}(i,x)_k},y\neq \mathrm{path}(i,x)_{k+1}}f_{y,j}\right)+g_{x,j}\right) \]

逐步转移就是

\[h(j)_{i,a}=\max\left(\max_{x\in \mathrm{son}_i}\left(j+\sum_{y\in \mathrm{son}_i,y\neq x}f_{y,j}+h(j)_{x,a}\right),[d_i=a]g_{i,j}\right) \]

显然 \(f_{i,j}=h(j)_{i,d_i-j}\)。而 \(h(j)_{i,a}\) 是可以长剖、打标记维护的。因为要对 \(O(m)\)\(j\) 各做一遍,总复杂度 \(O(nm)\)

T-085 P9482 25-12-10 B

因为需要快速比较字符串的两个子串的大小,想到 SA(在本题中,就是把 \(s+R(s)\)(+ 代表拼接)后缀排序)。接下来,只需关心有多少以 \(j>i\) 开头且 \(j\) 是奇数/偶数的前缀(原串的前缀),比以 \(i\) 开头的后缀,排名要大。称这样的 \((i,j)\) 为满足限制一的有序对。则原问题等价于询问,有多少 \((i,j)\) 满足 \(j<r\)。但该做法有问题,因为若 \(s[i:\frac{i+j}{2}-1]=R(s[\frac{i+j}{2}:j])\)\((i,j)\) 满足限制一,则虽然 \((i,j)\) 不满足原题面的要求,但我们认为其符合要求,算多了,不过能够通过 B 性质。自然想到,用错解的答案,减去算多的个数。现在目标就是求算多的个数。

被算多的情形,类似于一个回文。可以用 manacher 去描述原序列(及其子串)的一切回文有关信息,设 \(w_i\) 表示 \(i+0.5\) 处为中心的最大回文半径。先枚举 \(i\),则所有的 \(p>i,p-w_p<i\)\(p\)(称为合法的 \(p\)),都给出了如下限制:\(j\neq 2p-i\)。看性质 A,此时回文子串数目较少。故每当枚举 \(i\) 后,暴力检查所有合法 \(p\) 对应的 \(j=2p-i\),其 \((i,j)\) 是否满足限制一。若满足则去掉其贡献即可。如果想要直接用数据结构去除算多的部分,不可避免地要询问“有多少 \(j\) 满足,前缀 \(j\) 比后缀 \(i\) 排名大,且 \(j\) 等于某个合法 \(p\) 对应的 \(2p-i\)”(把这种“用数据结构硬维护”的做法,称为“摁做”)。

原询问是一组本质相同问题,尝试递推。那么从大到小扫描 \(i\),每次加入新的合法 \(p\),并删除刚刚变得不合法的 \(p\)。接下来有两种做法。法一,维护对于当前 \(i\) 来说,\(p\) 造成的不合法位置 \(j\)\(i\) 变小一后,这些位置均增加一,类似于 bitset 的左移。直接用 bitset 搭配预处理就能够通过本题。但是其复杂度不优秀,也不是我要讲的重点。

法二。若不上 bitset,常规数据结构摁做不了的。那怎样去处理呢?分析性质。也就是问,有没有更简单的方法,去描述对“被算多”\(j\) 的限制?刚才那个表述,是有两个限制嘛,第二个限制跟回文有关,应该是非 manacher 不可,不好改变。只有改变第一个限制。凭什么第一个限制能够变简单?因为第二个限制要首先成立,只有当 \(j\) 满足了限制二,才会被你送去检测限制一。换句话说,我们要分析的就是,满足了限制二的 \(j\),在何种条件下,才会满足限制一。\(j\) 满足限制二意味着 \(s[i:j]\) 是回文,那就要充分利用回文的性质。考察前缀 \(j\) 和后缀 \(i\),如何比较其大小关系?比较第一个不同的位置。这是回文呀!第一个不同的位置在哪里,在 \(\frac{i+j}{2}-w_{\frac{i+j}{2}}-1,\frac{i+j}{2}+w_{\frac{i+j}{2}}+1\) 嘛。也就是说,对于一个确定的 \(p=\frac{i+j}{2}\),它对应的所有 \((i,j)\) 是否满足限制一,只和 \(p\) 有关。不妨把“\((i,j)\) 满足限制一”作为合法 \(p\) 的另一个要求。这样,查询算多了多少,只需问当前有多少合法 \(p\) 即可。

T-087 CF2174D 25-12-10 A

调整法和描述法的综合应用总结一下。“不能构成一棵树”,此限制也非常非常弱。那跟上个题一模一样的啊,用调整法。先选择最小的 \(n-1\) 条边作为初态。若不构成一棵树,则直接输出即可。反之,称初态为“原树”,开始调整。调整是改变边集,相当于分两步,第一步是删除某些已有边,第二步是加入某些未有边。对一次调整应用描述法,只需记录删除了哪 \(k\) 条边、加入了哪 \(k\) 条边。接下来,对一次调整应用调整法(一步调整,也是一个方案,也有自己的描述法,也可能在接受微扰后,变成更优的方案。用“微扰”表示对调整的调整),从而找出最优调整的性质。

我一再强调,描述法相当于定死了(钦定)一个方案的全部信息,在最优化/构造问题中,如果知道了最优描述法(最优解/可行解一定被它描述),那只需找出一种方法(可以是 dp,也可以是简单枚举等等),去遍历最优描述法的所有可能性,就做完了。注意,描述法只和主体,以及你观察出的,这个主体的性质有关。但有时很难一眼看出最优描述法,这一般是因为你没有掌握足够的性质。此时,应该先找出一个很笨的描述法(不依赖什么性质,只跟主体有关,虽然可能性过多,但是最优解/可行解一定被它描述),然后把某些定死了的信息“放生”,去把“放生”了的这部分信息调整到最优(当然也可不用调整法,而是直接贪心),看看所得到的结构必然是怎样的(得出性质),据此对描述法略作修改。之后再“放生”另一部分信息,继续调整到最优、更改描述法。循环往复直到描述法可能性足够少为止。其实,T-081 的解题流程就近似于“逐步修改描述法”。

在本题中,不妨先令被删除的 \(k\) 条边在微扰前后不变(微扰法暂不涉及这部分信息,也就是先把它们看作“定死了”),则原图无论如何都是先裂成了 \(k+1\) 个连通块,把连通块缩点。后来加入了 \(k\) 条边,最终构成基环树和树的混合森林。我们去微扰“后来加入的边”。首先,混合森林中最多有一棵基环树,否则可微扰:保留某棵基环树,令其余部分不再加边,而是按照原树边合并成尽量大的若干连通块,不劣。所以,混合森林中恰有一棵基环树,换句话说,把连通块缩成点后,所加的边,应该连成一个 \(k\) 元环,称为环 \(l\)(还有一个点是孤立的,孤立点称为 \(z\))。仍然做不了,这意味着现在的描述法仍不够好,需要把不优秀的那些环,从描述法中剔除掉。

注意到原树是边权很小的,而且你比较掌握(总比随意选用边要好),在微扰的过程中,应多利用原树边。前面的调整和微扰,为什么如此大动干戈,就是因为初始描述法太劣。而现在可精雕细琢,减小微扰的幅度。微扰时,也必然要先删几个边,再加几个边。可以在加边的过程中利用原树边。从小往大考虑微扰幅度,不妨让一次微扰,只改变一条边。假设知道删了哪条边(本文中,“假设”这个词,就提示着描述法中“定死”的部分是什么),则重新加边时,除了不准加“某点-\(z\)”的原树边(变回一棵树了),加上任一原树边(在 \(l\) 上来看,有点像“弦”)来完成微扰均不劣。这意味着,最优的 \(l\) 不含原树边“弦”,故最优情形下,原树边必然是由 \(z\) 连向 \(l\) 内所有点的菊花形状。还不够。现在让一次微扰,只改变两条边。\(l\) 断成两个链,不妨设其中某条链链端为 \(a,b\),则加边 \(e_{za},e_{zb}\) 不劣。故 \(l\) 的长度至多是二,否则可以应用上述微扰减小环长。

描述法好像已经足够简单。分类讨论,若 \(l\) 长度为一,数据结构摁做即可,你肯定会。关键是 \(l\) 长度为二的情况,居然摁做不了!还需要微扰。假设已知某个解,其中 \(l\) 上的点分别为 \(p,q\),则可能涉及到的边只有,\(p,q\) 各自自环的最小边,称为 \(e_p,e_q\)(除原树边外);\(p,q\) 之间最小边和次小边,称为 \(e_{pq},e_{qp}\)(除原树边外);\(e_{zp},e_{zq}\) 是原树边;\(e'_{zp},e'_{zq}\)\(z,p\)\(z,q\) 之间除了原树边以外的最小边。该解就是 \(e_{pq}+e_{qp}\),对其的调整只有四种,是 \(e_{zp}+e_q,e_{zq}+e_p,e_{zp}+e'_{zp},e_{zq}+e'_{zq}\)。若调整无效,则该解在 \(z,p,q\)“定死”条件下最优,这需要 \(e_{pq}+e_{qp}<e_{zp}+e_q\)。因为 \(e_{zp}\) 小于涉及到的一切边,可放缩得到,\(\max(e_{pq},e_{qp})\le e_q\)。把四个式子都写一遍然后合并得到 \(\max(e_{pq},e_{qp})\le\min(e_p,e_q,e'_{zp},e'_{zq})\)。因为当初计算 \(e_{pq},e_{qp},e_p,e_q,e'_{zp},e'_{zq}\) 时实际上考虑了原图内的所有非树边,所以 \(e_{pq},e_{qp}\) 分别为最小的两条非树边。已经知道,如果要成二元环,是哪两条边成的环了,可以摁做。

T-088 P3148 25-12-11 D

非常非常简单的题目。在此处,认为原题面中的 \(R,C\le 100\) 是被数据保证的。

对于同一碎片,总共只有常数种不同的方向,方向不是重点。可以枚举选取了哪些碎片、碎片分别放在哪里,这是一个笨拙的描述法。可分析性质,以雕像中有颜色的点为主体,从边界元素考虑,也就是关注雕像的边缘、顶点。定义左上角为最靠左的那一列有颜色的点中最靠上的点,可得必然有一个碎片的左上角,和雕像的左上角重合。不妨先枚举是谁重合,则无需描述该碎片放到了哪里。现在待解决问题是“把两个碎片填入原图,使得原图的剩余部分恰好与此两碎片之一重合,且所有三碎片之间不能有交”。

关注问题的自递归性质,待解决问题基本上还是“若干碎片拼原图”的形式。具体来说,将最先枚举的碎片与原图重合的有颜色点,都在原图上改成空白后得到新图,问题就严格转化为“两个碎片拼新图”的问题,与最初“三个碎片拼原图”完全相同。那采用同种做法,只需枚举新图的左上角和谁重合,就转化为“一个碎片拼新新图”的问题,这个问题可以哈希解决。时间复杂度 \(O(K^2RC)\),可以通过。如果 \(R,C\le 500\),卡常即可。

T-089 P9479 25-12-11 A

由第一条限制,\(T\)\(T'\)虚树。那么 \(m\le 2\) 的部分非常容易,直接思考 \(n=1,k=0\) 的部分分。最简单的描述法是,记录每个节点的父亲是谁,对应了树形 dp 的常规模型,从子树信息合并的角度入手定义状态,结果发现完全做不了。

关注限制。因为要求 lca,所以限制二和树的形态有关。它还和节点编号的相对大小关系有关。对于这种限制,有两种考虑方法。一种是以树的形态为基础,另一种是以节点编号为基础,也就是以值域为基础。常规树形 dp 就属于以“树”为基础,但没有前途。那以值域为基础如何处理呢?只涉及相对大小关系呀!没想法吗?

扫描一个值 \(x\in[1,n+m]\)转 01 序列去写出限制。当然,此处其实是转 012 序列,是分别将编号小于等于 \(x\),编号属于 \((x,x+k]\) 和编号大于 \(x+k\) 的节点,重标号成 0/1/2。那么任两个 0 节点的 lca 必须是 0 或 1 节点。所以其实我只关心 0 节点内部的一切树上关系,可以用虚树去描述。所以限制是:所有 0 节点构成的虚树,只能包含 0 节点和 1 节点。

限制所关心的结构,就是所有 \(x\) 对应的 0 节点虚树。而最优描述法的主体,很可能是受到限制的主体。但如果要把这 \(n+m\) 棵虚树,以及其上节点的 01 性都记下来,这种描述法冗余信息太多。在此处讲一下逐步描述法,其实在 T-018 中就已应用过。逐步描述法用于处理,限制跟多个“时刻”的状态都有关系,不过这些状态具有可递推性的问题。只需每次在上一个状态的基础上,再额外记录一部分信息得到本状态,不断用已经记录的信息对尚未处理的信息作出限制。具体来说,本题中就是从小到大扫描 \(x\)\(x\) 增加一时,0 虚树只会在某边上添加一个 0 节点,也可在某点下挂上一个新 0 儿子。或者增加两个节点,一个 0 和一个 1,其中 1 是 0 的祖先,且 1 在原虚树的某边上。还可能不增加节点,而是将一个 1 变成 0。那么改换描述法,只需记录总计 \(n+m-1\)\(x\) 增大一时,对虚树的“修改操作”是什么即可。实际上,因为 \(x=n\) 时的虚树形态被原题给出,那么让 \(x=n+1\) 开始扫起,只需记录 \(m-1\) 次操作(据此看出,这个问题也具有近似自递归的性质)。

描述法足够优秀,直接 dp,转移时提前确定信息(添加一个 0 和一个 1 节点时,提前确定该 1 节点是谁)。定义状态 \(f_{i,s}\) 表示当前 \(x=n+i\),编号在 \([i+1,i+k]\) 的节点中,已经在本虚树中的节点,长成 \(s\) 这个样子。转移已经讲过,就不提了(解法居然和原树的形态无关)。

T-090 P7220 25-12-12 B+

有两种做法。法一是从子任务一个一个思考过来的。前两个子任务比较简单。看第三个子任务,能够发现 \(x_{i+1}\ge x_i,y_{i+1}\le y_i\) 的性质在操作过程中始终保持。故只需用线段树二分出,每次扫地操作影响到的灰尘编号区间。对这个编号区间,将 \(x\)\(y\) 统一赋值即可。

对于第四个子任务,有两种做法。法 I 是,认为本题的关键在于元素合并,扫地操作会造成若干元素位置重叠,重叠的元素在后来的操作中受到的影响是一模一样的。发现元素在扫地的过程中自然合并,形成锯齿状结构。形式化地说,关注某条从左上角开始到右下角结束的格点路径,其中从“向右走”拐到“向下走”的所有拐点均位于 \(y=-x+n\) 上。只有这个锯齿状结构上才有重叠的元素。拐点是关键的,不妨将该结构按照路径所有拐点分开,形成若干条线段,对每条线段,如对于水平线段,开一个平衡树维护它上面的所有灰尘编号,这些灰尘在平衡树上依纵坐标排好序。一次扫地操作,至多只分裂一个线段、“压缩”一个线段,平衡树能够处理。不过还可能将一些从未加入锯齿状结构的“新灰尘”,加入某条线段(对于同一次操作,只会有一个线段被插入点,就是那个被分裂的线段)。要查询每次操作涉及到哪些“新灰尘”,需要树套树维护所有“新灰尘”的坐标,复杂度瓶颈就在于此,是 \(O(n\log^2 n)\),但其实已经能够通过子任务四。不过可以优化。刚才是以操作为主体,问有哪些新灰尘。可以对称一下,以一个灰尘 \((x,y)\) 为主体,只有在第一个涉及到它(形如 \(x\le l_i\le y\))的操作时,才会被加入锯齿结构。单点修改,区间查 min,即可知道每个灰尘第一次被涉及到的时刻。此时总复杂度 \(O(n\log n)\)

法 II,用化归思想,关注怎样利用子任务三的解法。锯齿状结构本身就满足子任务三所要求的性质,而任一灰尘被操作一次后,就会加入锯齿状结构。所以关键在于,一个灰尘第一次被操作的时刻是什么,我们已经会求了。剩下的直接套用子任务三即可。

对于整道题。关键在于这个加入操作,摁维护不了。不会加入就别加入。我多次强调过,规避删除操作可以用时间轴数据结构,其实规避加入也一模一样的,此处可以用线段树分治,把一个灰尘放置到 \(O(\log)\) 段时间线段树的节点上。用化归,关注子任务四的做法,能干什么。它能够处理,某些灰尘在时间 \(l\) 均存在,问它们经过时间区间 \([l,r]\) 的一切操作,会被移动到什么位置。我们在线段树分治 dfs 过程中,需要不断处理询问“挂在当前节点的灰尘 \(t\),经过当前节点代表的时间区间的一切操作,会被移动到哪里”,并将这个查询到的位置信息,放置到 \(t\) 所应被挂到的下一个线段树节点处。这种询问,可以通过对当前时间节点跑一次子任务四,统一回答。于是总复杂度 \(O(n\log^2 n)\),因为时限较大能过。

法二。形式化地说,一次操作是将 \(x_j\le l_i\le y_j\) 的所有灰尘 \(j\)\(y_j\)\(x_j\)(取决于操作种类)改成 \(l_i\)。用区间描述,所需关注的只有 \([x_j,y_j]\) 跨过 \(l_i\) 的灰尘。“跨过”呀,没想法吗?猫树分治。把每个灰尘挂到唯一一个中点被当前灰尘跨过,且不被当前灰尘包含的猫树节点上。一次操作只需处理,在猫树的节点上(该操作所访问到的 \(O(\log n)\) 个猫树节点都得处理),将挂在上面的区间(也就是灰尘),左/右端点小/大于 \(l_i\) 的那部分区间的左/右端点,赋值成 \(l_i\),这个可以用树套树解决。不过有一种可能,就是 \(l_i\) 的取值会让某些区间,在操作后不再应当挂在本节点。其实简单,因为区间只会收缩,所以一个区间至多“不再应当”\(O(\log n)\) 次,每发现一个“不再应当”的区间,就把它删除,然后重新放到它该去的地方即可。因为有“重挂”,所以内层树应使用平衡树。总复杂度 \(O(n\log^2 n)\)

T-091 CF2165F 25-12-13 B+

目标是找出极小的包含 21435 的区间。我多次强调,需要你找出极小区间的问题,关键结论一般是,极小区间只有 \(O(n)\) 种。举一个例子,就是“极短 mex 区间只有 \(O(n)\) 个”。注意本题的限制只涉及到相对大小关系,可以扫描一个值 \(x\) 转 01 序列,将大于等于 \(x\) 的元素记录成 1,反之为 0。则你的 21435 子序列,扫描下来共六种形态,分别形如 00000,01000,11000,11010,11110,11111。

21435 子序列还是太复杂,我一再强调复杂的东西可以去想拆开,所需要处理的只有,各个部分自己内部元素间的关系,以及部分之间的关系。各个部分内部的关系好不好处理,在我们决定“如何拆”时不太重要;重要的是,部分之间的关系必须好处理。在本题中,部分之间不仅有位置关系,还有数值关系。位置关系最好是“\(x\) 部分元素位置,整体大于 \(y\) 部分的”,数值关系最好是“\(x\) 部分元素值,整体大于 \(y\) 部分的”。综合来看,按照 00/111 将原子序列分成两段,位置和数值关系均好处理。换句话说。需要处理包含 21 子序列的极小区间,以及包含 435 子序列的极小区间,合并它们得到 21435 极小区间。还是应该先关注部分之间的关系,在此体现为“如何合并”,也就是说应该先搞清楚如何合并,再想各个部分内部如何处理。合并时,可以枚举极小 21 部分,在后面找出满足 3 数值大于所枚举 2 数值且 4 位置大于所枚举 1 位置的 435 部分中,5 位置最小的那个,则此二者合并得到 21435 极小区间。

现在分析部分内部的关系。对于 21 部分和 435 部分,也是在找极小区间呀,那根据关键结论,它们是不是各自也仅 \(O(n)\) 个呢?先考虑什么叫极小,其定义和你合并的过程有关,合并过程中只涉及到 1,4 的位置关系,以及 2,3 的数值关系,还有 2,5 的位置。因此,21 极小区间包含了三重意义,分别是 2 位置尽量大、2 数值尽量小、1 位置尽量小。435 极小,也是三重意义,4 位置尽量大、3 数值尽量小、5 位置尽量小。先分析 21 极小区间,枚举重要的 2 元素,则 1 元素必然选择位置在它之后的第一个小于它的元素,总计 \(O(n)\) 个。关键是 435 部分如何处理。

其“极小”的定义有三维,任两维之间没有关系,所以对于两个 435 部分,除非一个严格优于另一个,否则两个部分都得保留。定下一部分信息,剩余信息做贪心,注意其对称性,要把三维都考虑到。具体来说,在 4,3 元素相同时,只需选择 5 位置最小的;4,5 相同时,选 3 数值最大的;3,5 相同时,选 4 位置最大的。随便枚举某元素,如枚举 4,去处理上面的要求,你看啊:在“位置”轴上建立 01 序列,将大于 4 数值的元素标成 1,小于它的标 0。由要求一,假设枚举某个 0 位置作为 3,则 5 位置就是其后第一个 1。因此,以 5 位置为主体,则它前面紧贴的那个连续 0 段都会选它,故由要求二,这个 5 只需选择前面连续 0 段中数值最大的元素作为 3。因此,每个连续 0 段只会产生一个有效 3,5 对。但是对于一个 4,共计 \(O(n)\) 段连续 0,对所有 4 不就成 \(O(n^2)\) 个极小 435 了吗?其实不是,因为还有要求三没有使用,对于一个连续 0 段,选择所有能够生成它的 4 中位置最大者。在 4 数值从小到大扫描的过程中,总计只产生 \(O(n)\) 个不同的 0 连续段。故 435 极小区间共 \(O(n)\) 个。

此时仅 \(O(n)\) 个 21 和 435,合并时关注两维信息“2,3 的数值关系、1,4 的位置关系”,扫描一维,另一维线段树摁做。据此找出 \(O(n)\) 个极小 21435 区间,就能 \(O(n)\) 算答案了。总复杂度 \(O(n\log n)\)

T-092 P7564 25-12-13 B

本来不是多困难的题目,但是不去画图就难以直观描述。所以我在此强调画图的重要性。一般来说,含有时间的问题,可以时间为一轴;含有坐标位置的问题,可以坐标为一轴;含有数值的问题,可以值域为一轴。而类似于区间的东西,可以让它只占一轴,见 T-090 法二;也可以让它占两轴,以其左右端点位置分别为一轴,见 T-083 题解区其它做法,我没有在本文记录。如果轴数太多,想想问题本质上到底和哪些轴有关,而把其它轴省掉。对于原有坐标系还可以做平移、旋转变换得到更合适的坐标系。

以时间为横轴、位置为纵轴,建立平面直角坐标系,则一个顾客的轨迹形如一条斜率为 \(\pm 1\) 的线段。对于保镖来说,从边界元素思考,其行动轨迹可由斜率为 \(\pm 1\) 的线段拼接而成,无需利用其它斜率。因此可旋转坐标系四十五度,变成所有人都要么为竖直线段,要么为水平线段的情形。把原来时间加一、位置加一的方向重命名为“向右”,时间加一位置减一的方向命名为“向下”。

子任务一就是直接 \(f_{i,j}\) 表示保镖当前在 \((i,j)\),只准自此处向右或向下行走,能够获得最大工资时多少。对于其它子任务,状态肯定爆炸,但是这个 dp 已经非常简单了,没有办法优化。注意只有 \(n\) 数据范围小,尝试离散化,只关心横、纵坐标,分别和某个顾客的起始断点或终止端点的横、纵坐标相同的那 \(O(n^2)\)关键点,用上述做法预处理,保镖初始位于任一关键点的最大收益。而对于初始不在关键点上的保镖,想办法处理他行走的前一点点,使之转化为“从关键点开始”的问题。也就是说,考虑他第一次到达关键点之前,是如何走的。他只可能先向右或向下走一段,然后沿着某个顾客一直走,直到走到某关键点。分析一下,这个过程可以李超树摁维护。

T-093 P8329 25-12-15 C

从小到大依次确定当前节点是否是树 1 中的叶子,以及当前节点在树 1 中的父亲是谁。至于当前节点在树 2 中的父亲是谁等信息,可费用推后计算。具体来说,定义状态 \(f_{i,j,k,l}\) 表示确定了前 \(i\) 个节点,在这些节点中,有 \(j\) 个被确定为“树 1 非叶子”,但其编号最小的儿子还没有被访问到;有 \(k\) 个被确定为“树 1 非叶子”,且其第一个儿子已经访问到;前 \(i\) 个节点的树 2 父亲,构成一个大小小于等于 \(i\) 的不可重集,其中编号大于 \(i\) 的元素有 \(l\) 个。转移比较容易,是 \(O(1)\) 的,总复杂度 \(O(n^4)\),可得 60 分。

如何去优化状态呢?用排除法。这个 dp 没有冗余状态,无法把 dp 过程拆开(我曾尝试把 \(l\)\(j,k\) 分开 dp,没有成功),无法格路计数(看到转移“步长”很小,想到格路计数,然不行),无法断点转移,无法做费用提前/推后对称,更不可能数据结构优化。你想,这是完全对称的问题,为什么树 1 要记录两个额外状态 \(j,k\),但是树 2 只用记录一个 \(l\) 呢?因为树 1 需要额外处理“每个树 1 非叶子都有儿子”的限制。不妨修改一下这个限制,而限制的转化,除了基本的对称/取反操作以外,我们只讲过容斥

假设知道每个节点是否是树 1 的叶子,如何计算方案数呢?若有 \(x\) 个树 1 非叶子,钦定其中仅某 \(y\) 个可能有儿子,其它必须没有,容易 dp 计算满足该钦定的答案数,记作 \(f(y)\)。推一下容斥系数,答案就是 \(\sum (-1)^{x-y}f(y)\)。回到原题,把容斥系数在 dp 过程中体现。定义 \(f_{i,j,k}\) 表示,确定了前 \(i\) 个节点,在这些节点中,有 \(j\) 个被确定为“树 1 非叶子”,且被钦定可能有儿子;前 \(i\) 个节点的树 2 父亲,构成一个大小小于等于 \(i\) 的不可重集,其中编号大于 \(i\) 的元素有 \(l\) 个。转移时,若确定当前节点是“树 1 非叶子”,并钦定它可能有儿子,向 \(j+1\) 转移;若钦定不能有儿子,则乘上 -1 的系数向 \(j\) 转移。转移 \(O(1)\),总复杂度 \(O(n^3)\)

T-094 QOJ15309 25-12-15 C

\(g(p)\) 相等的排列称作“等价的”,则 \(k\) 个排列被分为若干等价类,问等价类个数。以一个等价类为主体,其贡献就是 1,无需关心不在其中的排列。但关键问题是,只有极大等价类才能贡献 1,而极大等价类的子集,不能做贡献。这可以用容斥去解决,也可以不用。

等价类是由 \(g(p)\) 决定的,以某个 \(g_0\) 为主体,问其贡献。法一,不用容斥。则一个大小恰好为 \(x\) 的极大 \(g_0\) 等价类,出现概率就是 \(P_x(g_0)=\binom{k}{x}P(g_0)^x(1-P(g_0))^{k-x}\)\(g_0\) 总贡献为 \(\sum_{x>0} P_x(g_0)\),这是二项式定理的形式,变换得到 \(1^k-(1-P(g_0))^k\)。现在求“给定 \(g_0\),问 \(P(g_0)\)”。定义排列 \(p\) 的逆排列为 \(q\)。看双元限制,\(\forall x\in g_0,\forall q_y<q_x,y<x\)。取反面就是 \(\exists x\in g_0,\exists q_y<q_x,y>x\)。据此,从大到小访问所有元素,只关心当前元素和所有比它大的元素之间的位置关系,若当前元素是某个前缀最大值,则它必须比所有访问到的元素位置都小;反之,必须比某个元素位置大。类似于 T-089,因为只关心所有大于等于 \(x\) 的元素之间的位置关系,只需一个长度为 \(n-x+1\) 的排列就能记录所有信息,当 \(x\) 减小一时,给当前维护的排列做修改,插入一个元素即可。若当前元素是前缀最大值,则只有一种插法,概率为 \(\frac{1}{n-x+1}\);反之,概率为 \(\frac{n-x}{n-x+1}\)。总概率 \(\frac{\prod_{t\not\in g_0}(n-t)}{n!}\)

但是仍不好求 \(\sum_g (1-(1-P(g))^k)\)。把里面的式子展开,是一个关于 \(P(g)\)\(k\) 次多项式,系数也是简单的组合数。那怎么求 \(\sum_g P(g)^a\) 呢?dp,令 \(f_i\) 表示,确定了前 \(i\) 小的元素是否为前缀最大值,\(\sum_g(\prod_{t\not\in g,t\le i}(n-t))^a\) 是多少。总复杂度 \(O(nk)\)

法二。如果做容斥,那一个大小至少为 \(x\) 的极大 \(g_0\) 等价类,出现概率就是 \(P_x(g_0)=\binom{k}{x}P(g_0)^x\)。据此容斥,总贡献中,\(P_x\) 的系数应为 \(F_x=1-\sum_i\binom{x}{i}F_i=(-1)^{x-1}\)。可直接推出用法一得到的最终式子:总答案就是 \(\sum_g\sum_x(-1)^{x-1}\binom{k}{x}P(g)^x\)。接下来还是得会求 \(\sum_gP(g)^x\),跟上面一模一样。

T-096 25-12-24 S

有一个初始为空的有标号无向图,从小到大依次往里添加编号为 \(i\) 的节点,每次添加时,连上 \(i\) 向编号小于 \(i\) 的节点的最终所有连边(不准有重边),记录第 \(i\) 次添点之后图中边双连通分量个数为 \(c_i\)。给定一数组 \(w\),据此定义一种方案的权值是 \(\prod w_{c_{i-1}-c_i+1}\)。要求最终图中恰有一个边双连通分量,求满足此条件的,共添加 \(a\) 个点的所有方案的权值和。对于 \(a\in[1,n]\) 均求出答案,\(n\le 200\)

直接状态压缩添加前 \(i\) 个点后的边双缩点树,肯定做不了,而且问题跟树的形态关系极大,很难直接抛弃树的形态做 dp。终止状态是确定的,所以倒序考虑,关注最后一次添加的点 \(a\)。它把原图合并成一个边双,这要求,在最后一次添点之前的边双缩点森林上,所有度数为一的缩合节点,都至少向 \(a\) 连一条边。特别地,如果是孤立点,至少向它连两条边。

像这样求加权方案数的题目,永远都是算权值比算方案数困难,其难点在于算方案数不太需要关注元素确定的顺序,但算权值的时候需要。一般有两种解决方法,一种是把权值用组合意义转化掉,变成单纯求方案数的问题。另一种是,算权值和算方案数交替进行,且以算权值为主体,每步“算方案数”主要是算“把前一次算权值后得到的元素组合起来”的方案数(当然还需要调用一下“前一次算权值后得到各个元素”这一子问题本身的加权方案数)。本题是第二种。把求得答案之前,最后一步“算权值”的步骤称作“最终权值计算”,最后一步“算方案数”称作“最终方案计算”。则“最终权值计算”只需枚举最后一步前的缩合节点个数 \(b\),就知道最后一步权值为 \(w_b\)。接下来总的目标,是求 \(g_{b,a-1}\) 表示“添加 \(a-1\) 个节点后,产生恰有 \(b\) 个缩合节点的森林。然后在缩合叶子上选择至少一个原节点,其余缩合节点上可选择任意多个原节点。特别情况是孤立点至少要选择两个原节点”的加权方案数(被选节点就是向 \(a\) 连边的)。按照所讲,现在应该处理“最终方案计算”,算“上一次算权值得到的若干元素”的组合方案数。而只有缩合边双的时刻才会计算权值,故待组合的若干元素,就是那 \(b\) 个缩合节点(小边双)。也就是说,把最后一步前缩合森林的树边,以及“选择原节点”,统一放到“最终方案计算”时确定。这没有问题,是因为我强调过的,算方案数不牵扯顺序。想严谨一点可以证“一一对应”,我就不证了啊。

具体来说,定义状态 \(f_i\) 表示给空图添加 \(i\) 个节点变成边双的加权方案数,则 \(f_i=\sum g_{j,i-1}w_j\)。所谓“元素组合方案数”就是“森林树边和选择”方案数,转化为算“树形态和选择”方案数,因此定义 \(h_{i,j}\) 表示给空图添加 \(j\) 个节点变成 \(i\) 个缩合节点的并“选择”的加权方案数,则 \(g\) 可以用 \(h\) 直接背包。关键是 \(h\) 如何求,先想到树形 dp,但复杂度过高。其问题在于直接树形 dp 对应的描述法,记录了冗余信息“树的形态”,而这么多信息对计算答案是不必要的。

描述法考察 \(h_{i,j}\) 的计算过程。首先你必定要调用每个小边双的 \(f_x\) 作为“上一次算权后解决的子问题答案”,故必须记录“第 \(i\) 个小边双的大小 \(a_i\)”(顺便算前缀和 \(s_i\)),那么子问题加权方案数的部分就是 \(\prod f_{a_i}\)。看来还需定义“谁是第 \(i\) 个”,也就是给原有点分配,其从属小边双的编号。不妨依据集合字典序比较出边双顺序,也就是说,令第 \(i\) 个小边双表示,删除前 \(i-1\) 个小边双内所有点后,编号最小点所在边双,据此得到编号分配方案数 \(\prod \binom{s_i-1}{a_i-1}\)。现在才进入最关键的“树边和选择方案数”联合计算的部分,笨拙地,直接描述 \(i\) 个点的某种有标号无根树 \(P_i\),根据树的情况得到编号为 \(i\) 的小边双是不是叶子,用布尔数组 \(v_i\) 表示。缩合叶子“选择”方案数为 \(2^{a_i}-1\),而缩合非叶子的是 \(2^{a_i}\)。则

\[h_{i,j}=\sum_a[\sum_{k=1}^i a_k=j]\left(\prod_k f_{a_k}\binom{s_k-1}{a_k-1}\right)\left(\sum_{P_i} \prod_k(2^{a_k}-v_k)\right) \]

在此介绍一个形似容斥的技巧,但本质是拆系数。注意看最后一部分,缩合之后是不是叶子,其系数式子只差了一项。对于某个已知的 \(v_i\) 状况,用直接数学乘开的方法,计算那个 \(\prod_k (2^{a_k}-v_k))\)。就是说,如果我们钦定每个 \(2^{a_k}-1\) 因式,是“选择”前面一项,还是“选择”后面一项,参与整个乘积运算,则把所有可能的选择之最终结果,加起来就是答案。形式化地说,枚举某个长度与 \(v\) 一致的 01 数组 \(s\),要求它里面 1 的位置是 \(v\) 中 1 位置的子集,则

\[\forall v,\prod_k(2^{a_k}-v_k)=\sum_s\left(\prod_k[s_k=0]2^{a_k}\right)\left(\prod_k[s_k=1](-1)\right) \]

\(s\) 的存在相当于,在树上选择一些叶子当“叶子”,算 -1 系数;剩下的都当正常节点,\(2^{a_k}\) 系数。改变计算顺序,先确定 \(s\) 再确定树形态,用 \(s\) 去写对树形态的限制\(s\) 中为 1(被当“叶子”)的节点,一定不能是任何节点的父亲,当然需要给它们各自找个在 \(s\) 中为 0 的父亲。至于 \(s\) 中为 0 的节点,无需关心它的后代情况,但要求所有 0 节点形成一个树上连通块。

连通块呀,没想法吗?prufer 图连通方案数。具体来说,把 \(s\) 中为 0 的小边双和为 1 小边双分开枚举,记录其大小数组分别为 \(a,b\),前缀和数组分别是 \(s,t\),因此可以暴力二维卷积计算 \(h_{i,j}\)。记录 \(q_{i,j}\) 表示 \(\sum_a(\prod_k 2^{a_k}f_{a_k}a_k\binom{s_k-1}{a_k-1})\),要求 \(a\) 长度为 \(i\)\(\sum a=j\);记录 \(r_{i,j}\) 表示 \(\sum_b(\prod_k -f_{b_k}b_k\binom{t_k-1}{b_k-1})\),则 \(h_{i,j}=\sum_k\sum_l \binom{j}{l}l^{k-2}l^{i-k}q_{k,l}r_{i-k,j-l}\)。至于 \(h_{:,1},h_{:,2}\),属于边界情况,需要额外讨论计算。这样直接枚举当前要求的答案 \(f_a\) 并每次调用计算 \(q,r,h,g\) 的函数,是总复杂度 \(O(n^5)\) 的。

注意我们每次是先计算出 \(f_a\),然后将它用于计算 \(q,r\),再用 \(q,r\)\(h\),用 \(h\)\(g\),用 \(g\)\(f_{a+1}\)。如果每次算出 \(f_a\) 后都从头再算,有重复计算。想得到的只有 \(g_{:,a}\),而为了得到它,需要调用但从未被计算过的 \(h\)\(h_{:,a}\),为了算它需要算 \(q_{:,a},r_{:,a}\),为了算它们需要考虑 \(f_a\)\(q,r\) 的贡献。所以每次算出 \(f_a\) 后,统一计算“大小为 \(a\) 的小边双”对 \(q,r\) 的贡献即可。组合系数可能跟直接计算的略有不同,重点是如何分配编号不会算重,也是做集合字典序比较就行了。算 \(q,r\) 复杂度 \(O(n^3\log n)\)(调和级数),算 \(h,g\)\(O(n^4)\)。注意因为 \(n\) 较小,杨辉三角预处理组合数会跑的更快一些,因为调用时没有取模。

T-097 一些 mex 题目 25-12-25 A

用这些题目来复习一下数据结构。

T-097-1

静态,区间查询 mex。

法零,莫队,但普通莫队要带一只 log。不过莫队维护答案时,删除操作比加入操作容易得多。不会加入就别加入,用回滚莫队规避加入操作即可。

法一,若离线做,可以扫描右端点,对于值等于 \(x\) 的元素,记录其最近出现位置存到值域线段树上,然后线段树上二分答案。也就是说,把等于 \(x\) 的数字分作一组,当成一种颜色,然后运用数颜色技巧。若用主席树维护各个版本,即可在线处理。

要注意,mex 不满足可加性,也就是给定两个集合,以及两个集合各自维护的有关 mex 的信息,想要查询这两个集合之并集的 mex 信息进而计算其 mex,是不够快速的。所以在常规数据结构题中的“分割阴影图形”技巧一般会失效。而且因为这个,mex 涉及到的一切东西不能做修改标记永久化。

T-097-2

单点修改,区间查询 mex。

不能用 T-097-1 中法二衍生的,树套主席树直接维护。因为若这样做,打在外层树的标记显然不能下传。故查询时需要将,外层树上某叶子节点的所有祖先处的,这共计 \(O(\log)\) 个用内层树维护的标记信息做一个合并,这是无法处理的。

法零,时光轴分块。对每一块,备份原序列,把块左端点之前的所有修改,都在备份上直接修改。在这里放个标记点,后面叙述方便。类似于 T-070,把本块内所有修改拆分为“删除,加入”,并将被删除过的位置,在备份上涂成 -1。接下来将本块内询问按右端点排序,从小到大扫描并维护 T-097-1 法一的值域线段树。每次查询时,还得把时间在询问之前的所有修改中的“加入”暴力处理,所以一次查询要涉及块长次的,值域线段树上二分。总复杂度 \(O(n\sqrt n\log n)\)

法零点五,上述做法的问题在于,值域线段树跟我们根号做法不搭。根号做法就得配值域分块。类似于 T-053,“先找出答案在哪一块,再找出它在块中的哪里”。标记点前面都跟法零一样,还是将本块内询问按右端点排序,从小到大扫描并维护值域分块,整块信息就是,块内每个值最晚出现位置的最小值。这个好像做不了呀,因为扫描时,任一值的最晚出现位置只会增大,你却要算最小值。这是给值域分块加入信息,因为不好做,把它对称删除

也就是说,标记点前面都跟法零一样,类似于 T-070,把本块内所有修改拆分为“删除,加入”,并将被加入过的位置,在备份上涂多个数(到时候扫描到该位置,就把你涂过的数全部加入值域分块)。依备份序列直接建立“扫描到 \(n\)”这一时刻的值域分块。将本块内询问按右端点倒序排序,从大到小扫描并维护值域分块,每次只会把元素的最晚出现位置改小,容易维护整块信息。每次查询时,得先把时间在询问之前的所有修改中的“删除”暴力处理,直接在值域分块上做临时修改,查询后再撤销临时修改,只需记录一下你修改了的那几个块的改前信息,就容易还原。接下来才做查询,从小到大一次看块,看整块信息是否够大,若不够就停下,当前块就是答案所在块。从小到大扫描当前块内的所有值,看其最晚出现位置,发现不够大的就停下,答案就是它。总复杂度 \(O(n\sqrt n)\)

法一,关注题中的所有,有值域、位置(这个其实是两维,因为查询为“区间”限制)、时间。值域轴如果放在里面,不可避免地要做信息合并,这不行。所以说,关于 mex 的数据结构题目中,大概率要把值域轴放在最外面。据此想到:你不是要二分嘛,只要允许离线,就可以整体二分。在整体二分递归到的,答案区间为 \([l,r]\) 的地方,先算 \(\mathrm{mid}\)。然后处理已经明确答案属于 \([l,r]\) 的询问(由整体二分的上一层求得),以及所有涉及到值域 \([l,\mathrm{mid}]\) 的修改(修改包括删除和加入)。此时,查询的目标是,看看它的答案是不是大于 \(\mathrm{mid}\),这要求 \([l,\mathrm{mid}]\) 的“颜色”都于询问时刻 \(q_t\),在询问区间 \([q_l,q_r]\) 内出现过。用线段树分治搞掉时间轴,则最内层就是 T-097-1,允许离线。总计 \(O(n\log^3 n)\)

法二,前面几个都是离线做法,复杂度还贼高,这就不好。关注 mex 的定义,直接找出“最小的未出现的非负整数”,有两个条件,一个是“未出现”,另一个是“最小”。看第一个条件,以整数为主体,做反面,若 \(\exists p\in[q_l,q_r],v_p=i\)\(i\) 不算“未出现”。也就是说若询问区间跨过任意一个 \(v_p=i\) 的位置 \(p\) 就不合法,那转化回来,\(i\) “未出现”的限制就是,询问区间是,相邻两个等于 \(i\)\(v_p\) 对应的 \(p\) 也就是 \([\mathrm{lst}+1,\mathrm{nxt}-1]\) 的这些“关键区间”中,某个区间的子区间。再看第二个条件,可得询问 \([q_l,q_r]\) 的答案就是它的,所有超“关键区间”中,对应 \(i\) 最小者。那么最外面直接扫描时间维,时间每变动一则“关键区间”变动 \(O(1)\),若它变动,相当于删除和加入 \(O(1)\) 个“关键区间”。用树套树处理即可。

树套树的维度如何选定?把两维位置分别放在外、内层,值域维度用所维护的信息(最小值)处理可以吗?因为要删除,故“关键区间”只能是单点改,而询问时平面查。因此外层节点 \([l,r]\) 对应的内层树上节点 \([L,R]\) 应该表示,所有左端点属于 \([l,r]\) 且右端点属于 \([L,R]\) 的“关键区间”其 \(i\) 最小值是多少,这个好像很难处理删除“关键区间”,因为当递归到内层叶子 \([c,c]\) 并删除了对应“关键区间”后,需要查询左端点属于 \([l,r]\),右端点为 \(c\) 的剩余“关键区间”中权值最小的是谁。如果右端点为 \(c\) 的“关键区间”有好多个,就毁了!若摁做,得在每个外层节点上,另维护一内层平衡树,常数过大。不过这个其实好处理,右端点有重复你不会做,那就让右端点没有重复。把两个“关键区间”右端点大小比较的过程,改成“右端点,值 \(i\)”这个 pair 比较的过程,让每个区间的右端点彼此不同。但因为右端点值域变为原来的平方,内层树的 log 也要乘两倍常数,仍非常差。

当然,若允许离线,可以把所有 \(O(n)\) 个“关键区间”全拿出来,提前按照“右端点,值 \(i\),左端点”三关键字排序,来编号。内层树就调用这个编号作为“右端点值域”,时间上不用乘以两倍常数了。

所以说,复杂度或常数,有时和树套树内外维度也有关系。那如果把值域放在最外层,每次线段树上二分查找如何呢?这样的话,内层是第一个位置维度,而另一个位置维,用所维护的信息处理。线段树上二分时,需要查询,值 \(i\) 属于 \([l,r]\) 的、左端点小于 \(q_l\) 的“关键区间”中,其右端点有没有大于 \(q_r\) 的。存在转极大,内层树维护的信息应是右端点最大值。可是删除好处理吗?非常容易,因为任意时刻,任两个“关键区间”左端点不同。不用离线排序和离散化,就能减小常数。

我再次强调,mex 问题,值域轴放外面,大概率没错。

T-097-3-1

静态,查询某区间所有子区间的 mex 之和。

子区间问题,没想法吗?扫描右端点 \(r\) 并维护所有 \([l,r]\) 的 mex,做历史和;或者类猫树分治,不过这个还是得扫描并维护 \([l,r]\) 的 mex,跟前面差不多。所以必须关心,\(r\) 增大一后所有 \([l,r]\) mex 的变化情况。首先,可能被影响的区间只有,左端点大于,\(a_r\) 上一次出现的位置的那些。其次,这些区间 \([l,r-1]\) 的 mex 还必须是 \(a_r\),不过这个容易,因为 mex 具备区间包含单调性,所以可能被修改的区间的左端点,是一个区间,二分一下就知道是哪个。接下来得把它们的 mex 改对。相当于扫描值域(又是把值域放外面),依次看还有没有区间的改后 mex 应该大于等于 \(a_r,a_r+1,...\),看来每一时刻,这些区间的左端点还是一个区间 \([a,b]\)。只有这些值的最后一次出现位置 \(p_x\) 有用。如果当前扫描的值 \(x\)\(p_x\ge b\)\([a,b]\) 都应该大于等于 \(x\);反之,\([p_x+1,b]\) 的 mex 恰好被修改成 \(x\) 而退出,而 \([a,p_x]\) 则接着参与扫描的过程。注意对于同一左端点,答案只会改大不会改小,所以 \(p_x\) 作为一个合法“反之”的位置,至多参与一次一切(也就是 \(r\) 从一到 \(n\) 的整个过程)修改。也就是说,“反之”情况合计 \(O(n)\) 次。在值域线段树上维护值的最后一次出现位置,每次修改时,在其上不断二分出下一个“反之”值 \(x\),做类断点转移就行了,总复杂度 \(O(n\log n)\)。转化为区间赋值历史和,好像没听说过。直接改成区间加历史和,即可解决问题。

T-097-3-2 CF1148H

跟 T-097-3-1 几乎完全一样,不过查询变成了问某区间,mex 等于 \(q_k\) 的子区间个数。那么根据 T-097-3-1 的思想,\(O(n^2)\) 的答案矩阵(存着所有 \([l,r]\) 的 mex),其实可被划分为 \(O(n)\) 个内部颜色均一致的矩形,把答案叫做“颜色”更好想象。查询就是问,答案矩阵的某子矩阵中,有多少颜色等于 \(q_k\) 的位置。还是把值域放最外面,对于每种颜色各开一个动态开点历史和线段树就行了(其实,想象一下扫描线求矩形面积并的过程,和历史和的过程很有相似之处)。不过不太对,它强制在线,所以得上历史和主席树,但它是区间修改,主席树区间修改标记不得下传。所以我们给每个颜色开一个“标记永久化历史和主席树”。

因为矩阵乘法强调顺序,所以矩阵流派完全不适用于标记永久化的情景,只能用“时间戳”流派。它的核心在于,记录“本子树最后一次被修改时刻”标记(也就是“时间戳”)。询问时,若访问到某节点,需要知道其标记对查询的影响,来代替原有 pushdown 操作。也就是要知道“本节点所有标记乘以迄今存在时长”,因为迄“今”难以在打标记时知道,差分一下,改成记“本节点所有标记乘以时间戳”。修改某节点后,也需要对祖先做贡献来顶替原有的直接 pushup 操作(因为正常 pushup 需要把子节点的版本手动对齐到父亲节点上,但时间戳流派,只计算版本对齐造成的贡献,而不真的对齐)。为了利用 pushup 操作去维护上述标记,另外记录“本子树涉及到的,标记乘以受标记元素个数和,的和”以及“本子树涉及到的,标记乘以受标记元素个数和乘以‘打标记时刻迄本子树时间戳的时长’,的和”。这两个标记也可说成是“只考虑本子树内标记,迄本子树时间戳的元素和”和“只考虑本子树内标记,迄本子树时间戳的历史和”。当然还有常规加法标记。

T-097-4-1

定义“极小 mex 区间”为,满足“将左端点右移一格”或“将右端点左移一格”后,均会使得 mex 减小的区间。可以证明这种区间共 \(O(n)\) 个,如何预处理?

还是把值域放最外面,依次求出 mex 为 0,1,2,... 的极小 mex 区间。首先直接找出极小的 0-mex 和 1-mex 区间。然后,从小到大扫描值 \(x\),先对存储所有候选极小 x-mex 区间的 vector,求出极小区间。通过依左端点排序,加上单调栈实现。那么已经求出所有极小 x-mex 区间,对一个这种区间 \([l,r]\),分别求出其距离左端点最近的 \(x\) 出现位置 \(p\),和距离右端点最近的 \(x\) 出现位置 \(q\),形成两个新的区间 \([p,r],[l,q]\),分别算一下两个区间的实际 mex。例如对于第一个区间,mex 是 \(y\),就把它丢到对应存储 y-mex 区间的 vector 里。

极小 mex 区间的用武之地,基本上和 T-097-3 介绍的东西一致。

T-097-4-2 P10169

注意 min*mex=0。也就是任一区间,min,mex 至少有一个为零。任意选用类似 T-097-4-1 或 T-097-3-1 的一种做法即可。

T-097-5

给定长度为 \(n\ge 2\times 10^5\) 的序列 \(a,b\)。初始化 \(f_1=0\),定义 \(f_i=\mathrm{mex}_{j\in[1,i)}[a_i+b_i\ge a_j][a_j+b_j\ge a_i]f_j\)。求所有 \(f\)。空间限制 \(O(n\log n)\)

本题有三个轴,分别是时间、值域、位置(两维)。类似于 T-090 法二,注意到 \(a,b\) 虽是两维,却表现出一维区间的性质,将 \([a_i,a_i+b_i]\) 全部看作区间,称为“题目区间”。则关于 \(a,b\) 的那个限制,本质上就是要求两个区间有交。接下来,若把时间轴放最外面没法做。所以优先把值域放最外面,这样的话,因为有 \(f\) 之间的转移,必须依次求出 mex 为 0,1,2,... 的 \(f\)。现在,可以先把时间轴放次外层试试。则从边界元素分析,先看如何求 mex 为 0 的所有 \(f_i\)\(f_1=0\),从反面,所有与 \([a_1,a_1+b_1]\) 有交的,其答案都不是零。那么时间上第二个为零的元素,就是与 \([a_1,a_1+b_1]\) 无交的,时间编号最小的元素。找出它后,再寻找与它们两个都无交的时间编号最小元素,一直找下去直到不存在合法元素,则答案为零的元素就求完了。把所有答案为零的元素删去,然后仿照上面的过程依次求答案为一、二等等的即可。每个元素只会被找出、删除一次。

重要的是如何快速求出,与所有已知区间不交的,编号(时间编号)最小的“题目区间”。把已知“题目区间”当作障碍,则显然它一定是某个空隙的子区间。重新从边界元素考虑,先找出 \([1,a_1),(a_1+b_1,n]\) 这两个空隙各自中,编号最小的“题目区间”,并进行比较,假设最优的位于 \((a_1+b_1,n]\)。之后,充分利用已经算过的信息,就不再算 \([1,a_1)\) 范围内的最小“题目区间”(因已算过),只需计算 \((a_1+b_1,n]\) 被分裂成的两个新空隙的,最小子合法“题目区间”即可。

换句话说,\(O(n)\) 次询问,编号最小的,是询问区间子区间的“题目区间”;\(O(n)\) 次删除“题目区间”。这是典型的树套树题目,有两种内外维度分配法。一种是把两维位置依次放在两维树上,用维护的信息处理编号维。和 T-097-2 法二需要处理的问题完全相同,比较麻烦的是,空间 \(O(n\log^2 n)\) 无法接受。那么树套树卡空间,90% 都是放弃动态开点,预离散化,内层建立静态线段树。就是说,某个特定内层树所可能涉及到的第一维位置,是可以离线获知的,且外层树上,所有第 \(i\) 层节点所对应的内层树,共计涉及到 \(O(n)\) 个位置,在本题中恰好成立。外层树可选用树状数组,也减小了一定常数。同样地,类似 T-097-2 法二后来提出的,改为把编号维放最外面,也行。

T-098 CF1209G2 25-12-29 A+

用描述法,完整的描述法需要记录,被改掉的有哪些值及其对应被改成的值。但是若只为统计答案服务,只需记录被改掉的有哪些值即可。这提示我们“逐步修改描述法”,只记录应被改掉的有哪些值,而“放生”它们应该被改成什么。也就是说,思考如下问题:假设已知哪些值会被改掉,问如何给它们改值,使得结果合法。

关注什么状态算作“合法”,分析双元限制。任意拉出来两个值 \(i,j\),用边界元素刻画限制,将等于值 \(i\) 的最小元素称作 \(l_i\),最大元素称作 \(r_i\),则 \([l_i,r_i],[l_j,r_j]\) 不能相交。现在分析,“合法”这一要求,对“改值”的具体限制,还是从双元,若 \(i,j\)\(i\)“未改”,\(j\)“被改”,且二者相交,则 \(j\) 必须被涂成 \(i\)。若均“被改”,且二者相交,那么它们必须被改为同一个值。换句话说,任取两组改之前值不同的元素 \(i,j\)(代称改之前值分别为 \(i,j\) 的全体元素)。若 \([l_i,r_i],[l_j,r_j]\) 相交,则它们改后的值必须相同。转化图论模型,将相交的值连边,则同一连通块改后的值必须相同,这要求我们在同一连通块中,选择一个元素个数最多的值保留下来(称这个元素个数为本连通块的价值),而剩余的值都得被更改。直接贪心可以通过 G1。

我一再强调有修改操作,就可以拆成删除加入,看看哪一种容易做,如果有明显容易的,就用时间轴处理方法,把另一种规避掉。在此处,加入操作比删除操作容易多了,外层用线段树分治。不过内层不能并查集暴力合并连通块。这就需要观察连通块的性质,先看单点性质。单点性质一般跟对象的某个有关,从位置轴上看,同一连通块在序列上体现为一个区间。回想加入值 \([l,r]\),会让连通块怎样合并,所有被 \([l,r]\) 包含的连通块,其价值都被新连通块“吸收”,与 \([l,r]\) 相交的至多两个连通块,在被“吸收”的同时,为新连通块的边界做贡献。因为要计算加入 \([l,r]\) 对价值和(最终答案)的影响,还得知道所有被“吸收”连通块的价值和,以及新连通块的价值(也就是被“吸收”连通块的最大价值)。大致是一个“区间求和”“区间求最大值”,用线段树能不能摁做一下呢?因为要区间求和,一个连通块内,只能有一个地方有“值”,应等于该连通块价值,是哪里呢,不妨让它是连通块内位置最小的地方。故建立两棵内层线段树,一棵维护序列上位置为 \(i\) 的元素所在连通块,位置最小的地方是谁;另一棵维护序列上位置为 \(i\) 的地方的“值”。改用主席树处理“外层回溯”操作,注意区间修改标记不得下传,可以做到 \(O(n\log^2 n)\),无法通过,也不能优化。

对连通块的性质分析不够,此时不能只看单点性质了,先看连通块之间的关系。连通块间无交。找出关系紧密的一对连通块,例如相邻的连通块来看,你会发现此二者一定紧贴。既然紧贴,不妨改为以缝隙为主体(在数轴上的连通块问题,尤其是紧贴连通块中,挺常见的一个主体)。一个缝隙是两连通块交界处的条件是,没有任何区间跨过它。而答案就是,所有相邻两个缝隙之间,出现次数最多的值的个数,之和。把同一种值的出现次数记录到该值第一次出现的位置,第 \(i\) 个位置的记录值称作 \(a_i\)。用线段树维护每个缝隙 \(i\) 被多少区间跨过,记作 \(c_i\)。那么相邻两个 \(c_i=0\) 的缝隙之间的 \(a_i\) 最大值之和,就是答案。用线段树摁维护即可。

T-099 P10779 25-12-29 C

仙人掌,有两种想法,一种是点双缩点建立圆方树,然后在圆方树上做 dp 等。另一种是,广义串并联图。法一,建立圆方树,把虚点的实儿子们,按照原仙人掌环上的某个断环为链的顺序排序。定义状态 \(f_{i,0/1}\) 表示考虑 \(i\) 的圆方树上子树,若 \(i\) 为虚点且其实儿子中有被选取的节点,则第二维是 1,反之为 0;若 \(i\) 为实点且被选取则为 1,反之为 0。在此情况下,最大独立集是多大。转移非常简单。但这不是我要讲的重点方法。

法二,广义串并联图。我一再强调,广义串并联图方法是要用删一度点、缩二度点、叠合重边这三个操作,把原图缩成一个点。其核心在于,对于所有被操作过的子图缩成的点或边,用点权或边权维护出该子图的答案(删一度点会把点缩到新点里,剩余两个操作会把两条边缩到新边里)。对于本题来说,应该维护什么信息呢?最基础地,必须维护“某缩后点/边”所代表的子图的最大独立集大小,因为答案就在问这个。关注“缩二度点/叠合重边”的操作,你会发现只有点权不够,因为不知道边的两个端点有没有被选取。关注“删一度点”的操作,只有边权也不够,因为没有办法把“一度点”的价值,直接接到边上(有可能,与“一度点”相连的节点是度数很大的点,可不敢把所有和它相连的边都加上这个“一度点”的价值)。所以边权点权都要有。定义状态 \(e_{i,0/1,0/1}\) 表示第 \(i\) 条“缩后边”代表的子图,其左端点选不选,其右端点选不选,造成的最大独立集大小(注意无论何时“缩后边”的端点只有确定的两个,这两个点不是“缩后点”而是原有点)。无论左右端点选不选,这个大小也不包含左右端点的权值。定义 \(a_{i,0/1}\) 表示第 \(i\) 条“缩后点”代表的子图,选不选那个“所有跟本‘缩后点’有关的边,在本‘缩后点’处的端点,原有点”,造成的最大独立集大小(注意,容易根据广义串并联图的缩合过程证明,跟本“缩后点”有关的边,在本“缩后点”处的端点,就只是那一个原有点)。转移比较容易就不提了。讲一下广义串并联图缩合的过程,需要开一个队列,不断往里面补充度数小于等于二的点,每次出队就把那个点处理了,完成“删一度点/缩二度点”。若在“缩二度点”建新边的过程中,发现被建边的两个点本来就有边,就在此时“叠合重边”即可。

T-100 P13275 25-12-19 A

答案是 \(\sum_P\sum_Q[P\cap Q=\emptyset][f(P)=f(Q)]\prod_{i\in P\cup Q}a_i\)。想要转换贡献主体或者改变求和顺序,你会发现只有 \(f(P)\) 的值能够被枚举,其它元素连枚举都无法接受。因此关注 \([f(P)=f(Q)=t]\),它是加在 \(i\in P\cup Q\) 全体元素上的,还挺复杂的一个限制,应该转换一下,没想法吗?对限制的转换,除了对称/取反,就只剩下容斥

分析一下就会发现,这个容斥不能仅仅引入“\(f(P),f(Q)\) 相等且均至少为 \(t\) 的超集的方案数”,而必须进一步引入“\(a\subseteq f(P),b\subseteq f(Q)\) 的方案数”。具体来说,定义 \(G(a,b)\) 表示

\[\sum_P\sum_Q[P\cap Q=\emptyset][a\subseteq f(P)][b\subseteq f(Q)]\prod_{i\in P\cup Q}a_i=\sum_P\sum_Q[P\cap Q=\emptyset][\forall i\in P,a\subseteq i][\forall i\in Q,b\subseteq i]\prod_{i\in P\cup Q}a_i \]

转换贡献主体,以 \(i\) 为主体,一个 \(i\) 只能位于 \(P,Q\) 其中一个内,而且只有当 \(a\subseteq i\) 它才能加入集合 \(P\),只有当 \(b\subseteq i\) 才能加入 \(Q\),如果两个都满足,它就任选一个集合加入。也就是说,

\[G(a,b)=\left(\prod_{a\cup b\subseteq i}(2a_i+1)\right)\left(\prod_{a\subseteq i,a\cup b\not\subseteq i}(a_i+1)\right)\left(\prod_{b\subseteq i,a\cup b\not\subseteq i}(a_i+1)\right) \]

这个很容易预处理,定义 \(f_s=\prod_{s\subseteq i}(2a_i+1),g_s=\prod_{s\subseteq i}(a_i+1)\)\(G(a,b)=\frac{f_{a\cup b}g_ag_b}{g_{a\cup b}^2}\)。而两个预处理数组都是可以 \(O(n2^n)\) 高维前缀和直接做的。

现在 \(G(a,b)\)\(O(1)\) 求出了,推一下容斥系数。定义 \(F(a,b)\) 表示“\(f(P)=a,f(Q)=b\) 的方案数”,称为“原问题方案数”。由定义,其对应“原问题贡献系数”\(p(a,b)\) 当且仅当 \(a=b\) 时是一,反之为零。故“容斥贡献系数”\(q(a,b)=p(a,b)-\sum_{c\subseteq a,d\subseteq b}[c\neq a\ \mathrm{or}\ d\neq b]q(c,d)\)。求系数的复杂度是 \(O(8^n)\) 的,成为了瓶颈。我一再强调容斥系数一旦验证正确性后,对它最好的优化就是打表。表格如下(第 \(i\)\(j\) 列表示 \(q(i-1,j-1)\)):

1 -1 -1 1 -1 1 1 -1 -1 1 1 -1 1 -1 -1 1
-1 2 1 -2 1 -2 -1 2 1 -2 -1 2 -1 2 1 -2
-1 1 2 -2 1 -1 -2 2 1 -1 -2 2 -1 1 2 -2
1 -2 -2 4 -1 2 2 -4 -1 2 2 -4 1 -2 -2 4
-1 1 1 -1 2 -2 -2 2 1 -1 -1 1 -2 2 2 -2
1 -2 -1 2 -2 4 2 -4 -1 2 1 -2 2 -4 -2 4
1 -1 -2 2 -2 2 4 -4 -1 1 2 -2 2 -2 -4 4
-1 2 2 -4 2 -4 -4 8 1 -2 -2 4 -2 4 4 -8
-1 1 1 -1 1 -1 -1 1 2 -2 -2 2 -2 2 2 -2
1 -2 -1 2 -1 2 1 -2 -2 4 2 -4 2 -4 -2 4
1 -1 -2 2 -1 1 2 -2 -2 2 4 -4 2 -2 -4 4
-1 2 2 -4 1 -2 -2 4 2 -4 -4 8 -2 4 4 -8
1 -1 -1 1 -2 2 2 -2 -2 2 2 -2 4 -4 -4 4
-1 2 1 -2 2 -4 -2 4 2 -4 -2 4 -4 8 4 -8
-1 1 2 -2 2 -2 -4 4 2 -2 -4 4 -4 4 8 -8
1 -2 -2 4 -2 4 4 -8 -2 4 4 -8 4 -8 -8 16

注意它是分形的结构。具体来说,任一长宽均为 \(2^i\) 的系数矩阵,可被拆成四个 \(2^{i-1}\times 2^{i-1}\) 的子矩阵(就是左上/左下/右上/右下四个),每个矩阵本质同构,只不过乘上了个系数,系数形如

1 -1
-1 2

因此,单个容斥系数可 \(O(n)\) 递归求解,不过递归求解的过程,也是二进制拆位的过程,故可知 \(q(a,b)=(-1)^{|a\oplus b|}2^{|a\cap b|}\)。现在可写出答案是 \(\sum_a\sum_b q(a,b)\frac{f_{a\cup b}g_ag_b}{g_{a\cup b}^2}\),总复杂度 \(O(4^n)\)。这个式子已经非常简单了,那如何优化?因为是二进制卷积的形式,用 FWT

但是这不是单纯的卷积,在卷积的过程中还乘上了系数,而且答案只有一个,不像正常 FWT 题目是要贡献到一个数组里。关注最难以处理的卷积系数 \(\frac{f_{a\cup b}}{g_{a\cup b}^2}\),给它起个名字叫做 \(t_{a\cup b}\)。难以处理就别在卷积过程中处理,那只能在卷积结束后处理了。这提示我们定义卷积 \(h_{i|j}\gets q(i,j)g_ig_j\),FWT 求出 \(h_i\) 后再算 \(\sum h_it_i\) 即可。但是 \(q(i,j)\) 的系数如何在卷积中处理呢?只有把它用 \(i,j,i|j\) 去表示,才能把它“吸收”进 \(g_i,g_j,t_{i|j}\)。可知 \(i\oplus j=2(i|j)-(i+j),i\&j=(i+j)-(i|j)\)。据此得到 \(O(n2^n)\) 的做法。

但只能过性质 B,因为涉及到询问 \(g_i\) 的逆元,可是存在 \(a_i+1\equiv 0\) 时,可能造成 \(g_i\) 无逆元的情况。先把 \(a_i+1\equiv 0\) 的位置在计算 \(g\) 的时候剔除掉,不让它对 \(g\) 做贡献。之后只需剔除直接算时不合法的部分,也就是存在 0 位于 \(i\)\(j\) 的超集,但不位于 \(i|j\) 的超集时的情况。这完全无法在 FWT 过程中维护,唯一的希望是,多记录一维状态表示“0 有多少个”。形式化地说,令 0 在集合 \(s\) 中出现次数为 \(z(s)\),则记录 \(h_{i,j}=\sum_a\sum_b[a|b=i][z(a)+z(b)=j]g_ag_b\)(这里的 \(g\) 已吸收过应有的系数)。答案就是 \(\sum h_{i,j}[j\le 2z(i)]t_i\)。但其复杂度肯定超过 \(O(4^n)\),无法接受。

答案统计的是,枚举 \(i\) 之后,\(a|b=i,z(a)+z(b)\le 2z(i)\) 的那些数对 \(a,b\)。关注 \(z(a),z(b),z(a|b)\) 的大小关系,发现 \(z(a),z(b)\ge z(a|b)\),故合法情况只有 \(z(a)=z(b)=z(a|b)\),只需求 \(h_i=\sum_a\sum_b[a|b=i][z(a)=z(b)=z(i)]g_ag_b\)。如何套用 FWT 处理这一问题?FWT 是个复杂的过程,无论你是分析题目性质还是题目过程,如果它太复杂,都可以尝试拆开,从单点/双元的角度去描述。本题中,原有 FWT 可拆为三步 \(g\to g',g'^2\to h',h'\to h\),因为是较为简单的或卷积,可以从组合意义的角度去理解整个过程:\(g'_i\) 是对 \(i\) 子集中元素的 \(g\) 求和,\(h'_i\) 相当于在子集中任取两个元素(隐含条件是,此两个元素的或至多为 \(i\))的 \(g\) 乘起来,再求和。\(h'\to h\)容斥,减去能够被别的 \(h'\) 计算的部分,从而还原出“子集中任取两个元素 \(g\) 乘起来,要求此两个元素的或恰好是 \(i\),再求和”。现在 \(h\) 的定义改变了,但分三步的思路没变。\([z(a)=z(b)=z(i)]\) 这一条件,不太可能在最后一步容斥掉,那只能强制要求它在前面就成立,也就是说,据此改变 \(g'\) 的定义为,\(g'_i=\sum_{a\subseteq i}[z(a)=z(i)]g_a\)。这能在高维前缀和 \(g\to g'\) 的过程中处理吗?高维前缀和也是挺复杂一个过程,但能被拆开。只关注在某一维 \(t\) 上的转移(假设 \(t\) 从低位到高位枚举)。那么在第 \(t\) 维开始转移之前,已经算好的 \(g'_i\) 的意义应为“\(i\) 的子集中,满足 \(z(a)=z(i)\),且最高的 \(n-t+1\) 维和 \(i\) 完全相同的元素的 \(g\) 之和”。把这个分析清楚,就很容易了。任取一步转移 \(x\to x|2^t\)(拆双元了),意味着 \(x|2^t\) 在转移后,就要接纳一部分 \(z(a)=z(x)\) 的元素,不过 \(z(x),z(x|2^t)\) 的关系还不知道,判断一下,如果二者相等,就正常转移;反之放弃转移就行了。至于 \(h'\to h\) 的部分,是完全对称的,就不提了。

FWT 或/与卷积,因为算法是最简单最特殊的 FMT,跟“转移矩阵”这一套代数表示的关系没有那么大,可以从组合意义去分析,其算法灵活度也很大。所谓“快速 gcd 变换 FGT”\(h_i=\sum_{\gcd(j,k)=i}f_jg_k\) 也是一样,得从组合意义和容斥的角度去理解。

在此,我有一小段话想说。你刚才看到的题解,思维台阶是矮而且多的,思路过程是大致平滑的,但是大多数选手做题时,“台阶”只有两三级,高而陡峭。他们的方法,叫我这个天赋不够的人来说,只能望而生畏。有一位上一届的学长,非常有希望争夺 NOI2025 金牌的,他 d1 比金线高,可是 d2t2(本题)因“灵感缺失”,没有想出,总分跟队线差了一点点。我深深为他遗憾,替他痛惜。可是嗟叹之余,我还要问一句:难道信息学竞赛真是“彩票竞赛”吗?四十年来,有多少基础实力足够的选手,因为所谓“灵感”功亏一篑!我想,虽然信息学竞赛必然有运气的成分,或许“有灵感”也是金牌得主的重要素养之一;但是像我这样脑子笨的 whk 选手,唯一在 OI 赛场上打出好成绩的可能性,只能是在日常训练中,尽量抛却“灵感”。你读了我这么多篇题解,看了我写的 The Art of the Induction,应该能够明白我的想法:扔掉一切“感受一下”“毛估估”“注意到”,让一道题目的解法,能够真正一步步从已知中推出来。只需掌握最基础的几个寻找性质的工具与方法,还须牢记必要的 trick,而其它一切都是靠不住的,因为你能保证自己在 NOI 场上一定具备“灵感”吗?我认为自己不能。我们的目标就是,对每一道会做的题目,能说出自己如何做出来的;对每一道不会做的题目,做“逆向工程”,尽力猜想人家是如何做出来的。猜得多了,总结一下所学到的新思路、新方法、新 trick,也就都会了。

有人说我太在意细节,他还说,其实 OI 本来不需要那么严格的推理,其实 OI 本来就需要“模糊”一点,就是需要“感受”一下,可我不认同。也许我太菜了,也许我剑走偏锋了,也许我的天分达不到 OI 的要求,也许我该回去学 whk,但是我不在乎,因为在 Exemplary Examples 的这 100 道题目中,我能看见自己对这一门学科的浅薄认知中,最绚烂的图景。我不喜欢学科技,不喜欢读论文,不喜欢“没人能想到”的奇异思路和奇异题目,但我喜欢研究一种方法,这种方法能让几乎零基础的选手,望见高水平大佬的项背。我对 OI 的热爱或许就建立在它的基础上,这种热爱恐怕没有竭尽的一天,因为我不知道的还太多了,用现有方法解释不了的题目也太多了,我永远在完善“总结”,完善“方法”,继续训练的道路上啊。其实哪一门学科不是这样呢?我自学 whk,也是这样想,也是这样做。如果你和我是同道中人,尽力把我们的思想发扬光大吧。让他们知道,我们有逻辑的选手,也能取得自己满意的成果!

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