Exemplary Examples II

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

导语见 Exemplary Examples I。

T-051 P5327 C

答案是求 \((u,v)\) 的对数,可先枚举 \(u\),计算对应的 \(v\) 有多少个,这个个数就是经过 \(u\) 的路径并的大小减一。这是一类本质相同的问题,有什么想法?

不可能改变贡献方式(若改变,就是以 \(v\) 为主体,还是原来的问题,没有意义)。那如果是同时计算若干答案,在树上路径这一类问题中,就相当于是重链剖分,每次将一个路径对一段区间的贡献进行处理,但是这里很困难。那递推如何?树上路径问题中,递推就相当于是进行树上差分,因为在树上,一般就是利用子树信息去递推父亲(从差分数组还原原数组时,父节点需要用到子树信息)。那就是说,将路径在 \(s,t\) 挂上“加”贡献,在 \(\mathrm{lca},\mathrm{fa_{lca}}\) 挂上“减”贡献,每个节点合并子树信息,再结合挂在当前点的贡献,求出过当前点路径并的大小。

看来这个子树信息非常复杂,只能用线段树合并维护。子树信息记录什么呢?最直白的想法就是记录当前点路径并集里面有什么,放到线段树上就是,每个叶子节点如果是一就代表它在并集里面。如果是这样,它就还要支持“路径覆盖”“解除路径覆盖”。路径不好处理,那就把它重链剖分之后拍到 dfn 序上,就能转化成“区间覆盖”“区间解除覆盖”问题,扫描线即可。

T-052 CF453E C

分块做法比较简单,就不讲了。首先这是一个类似于区间覆盖的数据结构题目,可以 odt。那问题转化成,询问一个区间内所有小马恢复相同时间 \(t\) 后的法力之和。这不就是询问某版本(每个时刻建立一个新版本)中的区间和嘛,可以使用主席树预处理。

从小马为主体看,一匹小马的法力和恢复时间的关系是一个分两段的函数,那这个拐点很关键,在拐点前后的版本中,对当前小马的处理方式肯定不同。

从版本为主体看,当前所有马就分成两类,一类是“拐点前”的,一类是“拐点后”的。对于第一类,答案是 \(\sum_{i\in [l,r]} r_i\times t=t\sum_{i\in [l,r]} r_i\)。对于第二类,答案是 \(m_i\) 的和。看来这两类之间没有关系,唯一的关系(小马会在两类之间转化)可以简单维护。把它们拆开,就相当于建立两棵主席树,一棵维护当前时刻处于拐点前的小马的 \(r_i\),另一棵维护拐点后小马的 \(m_i\)。如果当前时刻有小马发生了类型转换,对两棵主席树分别新建版本来修改即可。

T-053 P4119 B

非常非常简单的题目。我一再强调,我说分块题要学会预处理嘛,比较经典的有“从块 \(x\) 到块 \(y\) 的答案”“前 \(x\) 个块的前缀和”。那这道题如何去处理呢?有询问和修改,先看只有询问的部分分。

区间第 \(k\) 大呀,二分。那就要知道 \([l,r]\) 内,小于等于 mid 的数有多少。通过树状数组维护刚才说的“小于等于 \(x\) 的数在前 \(k\) 个块出现多少次”,并对散块特殊处理,可以得到 \(O(n\sqrt n\log n)\) 的树状数组上二分做法,修改也可在相应复杂度内处理,但过不了,要消 \(\log\)

那就是值域分块,维护“小于等于 \(\sqrt V,2\sqrt V,...\) 的数在前 \(k\) 个块出现多少次;\(x\) 在前 \(k\) 个块出现多少次”,当然,还要支持 \(O(1)\) 查询“每个位置是谁”,因为查询时会涉及到散块。这是一个有名的思想,先确定在哪一块,再确定在块内的哪里。具体如何查询就不提了。而修改怎么做呢?从整块修改看,维护那两个预处理数组比较容易,但是“每个位置现在是谁”不太好做。你想,整块修改会将同种数值的元素统一修改,那就把同种数值的元素合并,只需对于“初始数值为 \(x\) 的元素”记录它们现在是谁。不过从 \(x\) 修改到 \(y\) 可能会将两种初始数值不同的元素合并,可以并查集嘛。至于散块修改,就非常容易了。

T-054 P14061 B+

要问回文串,那可以 manacher 求出每个位置作为回文中心的最大回文长度 \(s_i\)。那每次询问只需贪心做,具体就是不断维护当前最后一个回文区间在哪里,求“接下来第一个与当前回文区间不交且 \(s_i\) 大于等于 \(k\) 的位置”。对于 \(k\) 全相同的部分分,每个点只有唯一后继(就是接下来第一个位置),可以倍增做到 \(O(n\log n)\)

这做法真的特别特别依赖 \(k\) 相同,但它又是仅有(因而关键)的特殊性质所以肯定跟正解关系非常大,那不如把 \(k\) 不同的询问分开考虑。倍增做法的预处理复杂度是单个 \(k,O(n\log n)\) 的,那显然只能对于一部分 \(k\) 应用这种做法。可依据 \(k\) 根号分治,对小于 \(\sqrt n\) 的应用。那先关注 \(k<\sqrt n\) 的部分,总查询复杂度在 \(O(q\log n)\),但总预处理复杂度为 \(O(n\sqrt n\log n)\)。为平衡复杂度,需把 \(\log\) 消去。

倍增问题,有两种分块平衡方法,分别对应了两种倍增方法。一种是以“步数”为状态,以“位置”为答案,处理“从 \(x\) 开始,跳 \(\sqrt n,2\sqrt n,...\) 步到哪里;从 \(x\) 开始,跳 \(1,2,...,\sqrt{n}-1\) 到哪里”。另一种是以“位置”为状态,以“步数”为答案,处理“从 \(x\) 开始,跳多少步跳出当前块;跳出当前块后第一个跳到的是哪里”,本题中两者皆可。

再关注 \(k\ge \sqrt n\) 的部分。从数值关系上看,这一部分的特点是答案特别小,可以暴力跳,总计跳 \(O(n\sqrt n)\) 次。暴力跳时只需知道“接下来第一个与当前回文区间不交且 \(s_i\) 大于等于 \(k\) 的位置”,可以数据结构上二分/二分加 st 表去求,得到总复杂度 \(O(n\sqrt n\log n)\) 的做法,但还是必须把 \(\log\) 消掉。

这时候你发现任何“值域分块”的尝试都毫无进展,归根结底是因为你非要 \(O(1)\) 查询,但 \(k\) 变来变去让这一点绝不可能,我再给你写慢一点啊,就是因为 \(k\) 值域太大了,或者说 \(s_i\) 值域太大了。那怎么把值域变小呢?这是一个只跟相对大小关系有关的题目,可以整数序列转 01 序列。那就是从小到大扫描当前 \(k\),将新的 \(s_i<k\) 的位置赋为零,每次只需询问“某位置后面第一个是一的位置”。接下来有两种做法。

法一是类链表并查集,就不提了。

法二是继续分块平衡复杂度。总计 \(O(n)\) 次修改,每次修改可以做 \(O(\sqrt n)\) 的预处理。可以预处理“若本位置 \(x\) 后面,和 \(x\) 同块的部分有一,就指向第一个一;当前块最后一个点后面的第一个一”,这也是一个有名的思想,块内/块外。其本质就是先处理块内的关系,处理完了之后块内就没关系了,可以合并起来,一致对外。

T-055 CF516E B+

这个问题是总体限制,元素之间的关系太复杂,不能拆成单点限制,可以图论去做。求“最少几天”,看着像一个 bfs 或者最短路问题,不妨设 \(\mathrm{dis}_i\) 表示 \(i\) 这个人最早开心时刻。首先设超源 \(s\) 向所有已经开心的人连零边。关注元素之间如何建边,这相当于问:若 \(x\) 恰好在 \(\mathrm{dis}_x\) 开心了,他能够让哪些 \(y\) 开心,分别是在 \(\mathrm{dis}_x\) 的多少时刻后?

\(x\) 是男生为例,分别在 \(n,2n,3n,...\) 时刻后,他会让别人开心,“别人”分别是 \((\mathrm{dis}_x+n)\mod m,(\mathrm{dis}_x+2n)\mod m,...\) 这些女生。虽然编号里面带 \(\mathrm{dis}\),但只需在求最短路的过程中建边就行。不过边数特别特别炸。

那就优化建图嘛。对同一个男生,他连出的边权和出点均呈无穷等差数列,可差分一下,只需连第一个女生,再让每个女生向她的后面第 \(n\) 个女生连边权为 \(n\) 的边。现在边数变成 \(O(n)\) 量级的,还是不可接受。那恐怕是有边无用

你看最奇怪的边是哪些?男生/女生各自连的内部边,全都是特别简单特别有规律的,虽然边数多,但应该好处理;男生和女生之间的边就不太正常,甚至是最短路过程中连出来的。你看谁最重要?题目给出的,且数据范围能接受的只有“初始开心的男生/女生个数”,这些初始开心的男生/女生应该很重要。何况他们的连边其实不能算是最短路过程中连出的,因为他们的 \(\mathrm{dis}\) 已知是零。把他们作为主体,则其它所有人的快乐最终都来自于一个初始快乐的人。分析一下,其它所有人连向异性的边都无用,因为他们的快乐之源,肯定能比他们更早让对应的异性快乐。

综上,只需每个初始快乐的人向异性连一条边。现在关键的就是这些边,它们涉及的端点只有 \(O(10^5)\) 个,可把这些端点作为关键点框架。不关键的是同性边,可以看看它们的总体长成了什么样子。例如对于男生来说,同性边是形如 \(\frac{n}{\gcd(n,m)}\) 个置换环,环间没有关系,能拆开。就是说重标号一下,同性边就能变成若干个“相邻两人连边”的真环。那只需把环上相邻关键端点之间的同性边缩成一条边即可。

T-056 P14068 B

特别简单的题目。这个问题分两步,第一步是选择桅杆,第二步是求答案。先看第二步,求答案跟无人机关系很大,关键就在于无人机何时上何时下。

无人机受到的最特殊的限制是什么?不能碰到桅杆。这是个总体限制,能拆成单点限制吗?可以,因为桅杆之间互不影响。再结合“尽量贴地”这个限制,一根桅杆对无人机的影响就是让它不能太贴地,换句话说,无人机在桅杆所在的 \(x_i\) 位置时,应该比桅杆 \(y_i\) 高一。结合“只能上一/下一”,这意味着无人机在 \(x_i-1\) 处至少高 \(y_i-1\),等等。综上,无人机 \(x\) 轴坐标小于等于 \(x_i\) 时,不能低于一条过 \((x_i,y_i+1)\) 且斜率为一的直线。只需求出所有桅杆画出的这种射线,在所有 \(x\) 求射线函数最大值作为“边缘”,则无人机就会沿着这个“边缘”飞行(不过当边缘降低大于一的时候,无人机只会降低一)。

那就要关注,哪一个桅杆画出了当前位置 \(x\) 的边缘,它必然是 \(x_i\ge x\)\(y_i-x_i\) 最大的那一个。假设知道选了哪些桅杆,求答案的关键元素就是所有选了的桅杆中,\(y_i-x_i\) 是后缀最大值的桅杆(桅杆按 \(x_i\) 排序后重标号),因为其它桅杆没用。没有用就不选,可以要求选取的每个桅杆都是严格后缀最大值,即 \(y_i-x_i\) 递减。

现在把第一步第二步统一 dp 处理。状态 \(f_i\) 表示,选择了 \(i\) 号桅杆,关注 \(x_i\) 及之前的塔楼,它们带来的收益最大是多少。转移比较简单,只需预处理一下 \(w_{i,j}\),它表示 \(i,j\) 两个桅杆之间没有其它桅杆,且无人机从桅杆 \(i\) 顶部飞向桅杆 \(j\) 顶部,途中撞塔带来的收益。

T-057 P13814 A

首先形式化地表示题意,记前缀和 \(s_i\),则相当于求 \(\min\lfloor \frac{s_i-s_{l-1}}{i-l+1}\rfloor\)

法一,因为式子类似于斜率的形式,可转化为求从点 \((l-1,s_{l-1})\) 到区间 \([l,r]\) 内点 \((i,s_i)\) 的最小斜率。斜率呀!考虑凸包。线段树静态维护,区间内点构成的下凸壳,每次查询三分即可。但这个方法不是重点。

法二,可二分答案 \(k\),则相当于判定 \(s_i-s_{l-1}\ge (i?l+1)k\),把跟 \(i\) 有关的放一边\(s_i-ik\ge s_{l-1}+(1-l)k\)。那相当于求 \(s_i-ik\) 的区间最小值。\(i\) 为主体看,是关于 \(k\) 的一次函数。要求出若干个一次函数在 \(k\) 处的最小值,以 \(k\) 为横轴建立李超树,外层套线段树处理区间查询。这是 \(O(n\log^3 n)\) 的做法,不行。

三只 log 分别来自于二分,线段树,李超树,要消 log 只能消二分的。那就是把它变成李超树上二分。线段树二分关键在哪里?要支持 \(O(1)\) 询问一个节点代表的横轴区间 \([l,r]\) 内,所有纵坐标最小值。李超树上,就是问节点区间内,直线纵坐标最小值,只需问节点区间 \(r\) 处的即可。问最小值,常常关注是谁取到的嘛,那关注这个在 \(r\) 处纵坐标比别的直线都小的线。

我一再强调什么叫关注一个元素,就是要关注它的某一条性质,或者关注它和别的元素之间的一个关系嘛。可以从这个元素所属的集合内元素普遍都有的性质入手,再“代入”这个元素。还要根据你要什么来找出相关的性质嘛,在这里,我们就是要这条线是谁,要知道这一点,肯定跟李超树有关系,可关注这线与李超树的关系(尤其是要关心这线的编号“存储”在哪里)。这线本身和李超树有什么关系?很难看出。那它所属的元素集合,所有“线”都具有什么性质(跟李超树有关的性质)?每条线都最多挂在一个李超树节点上。“代入”这个线,可以问:它挂在哪里呢?在询问节点的父亲处!代码有点细节,就不提了。

T-058 AGC009E A+

首先把过程分析清楚。这是一棵有 \(n+m\) 个叶子的 \(k\) 叉树结构,每个叶子代表一个初始数字,每个非叶节点代表其所有子节点求平均值后新写的数字。

关注答案,也就是关注最终得到的数字和谁有关,它只跟每个叶子节点的数字分配 \(a_i\),还有叶子节点的深度 \(d_i\) 有关嘛(答案就是 \(\sum a_ik^{-d_i}\),根深度为零)。深度呀!没想法吗?

不同的深度之间的关系不大,可拆开,一层一层考虑。当然,虽然关系不大,但为了存在合法树,还是有限制的,只需相邻两层中,下层全部节点数等于上层的非叶节点数的 \(k\) 倍。那么改变贡献主体为每一层,若第 \(i\) 层的“一”叶子节点个数为 \(b_i\),则答案是 \(\sum b_ik^{-i}\)

我一再强调,做题过程中不要忘记你干了什么,和要干什么,最好用草稿纸记录下来嘛。我已经把“擦数/写数”的过程分析清楚了,建立了一个模型(树),把这个模型用双元相互限制(把树拆成逐层的,相邻层间有限制)叙述了一下。现在要干的是,通过要求字典序最小,限制模型个数,使得合法的模型与结果数字一一对应,用 dp 实现计数。那很可能这个字典序是基于 \(b_i\) 确定的嘛,例如对答案相同的方案,钦定 \(b_i\)\(i=\infty\to 0\) 依次拼成的字符串,字典序大者被计数(这个字典序是试出来的,总共也只有几种)。

要转化成具体的限制,可以用调整法。对一个合法,但未知字典序是否最小的方案,可任取一个“一”叶子,例如是第 \(p\) 层的,把它替换成一个有 \(k\) 个“一”子节点的菊花子树,对整体的合法性无影响,且字典序变大。此替换需要额外 \(k-1\) 个“一”叶子,那就得从前面省出来 \(k-1\) 个。怎么省呢,选一层 \(q\le p\),只需 \(b_q\ge k\),在其中挑 \(k\) 个“一”叶子缩成 \(q-1\) 层的“一”叶子即可。那就是说,除了最后一个 \(b_i>0\) 的层可以有 \(b_i=k\) 以外,所有层都必须 \(b_i<k\)。但是这个好像不对,树的结构不仅跟“一”叶子有关系,还跟“零”叶子有关系,设计字典序的时候还应该考虑每层“零”叶子个数 \(c_i\)。其实跟上面完全一样的,可要求除了最后一层可以有 \(c_i=k\) 以外,所有层都必须 \(c_i<k\)

现在计数,dp 倒序依次确定每一层的状态,因为要满足二元相互限制,每一层节点数都得是 \(k\) 的倍数。又由于在填 \(i\) 层时已知更深层的情况,故只要枚举 \(b_i\)\(c_i\) 也跟着确定了。也就是说,只用记录 \(f_{i,j}\) 表示倒序考虑到第 \(i\) 层,用了 \(j\) 个“一”叶子即可。

T-059 CF2057F D

极简单的一道题。首先肯定是二分,转化为求共 \(q\log V\) 次询问,要达到最大值身高 \(x\),需要多少披萨。关注最大值,就是去关注是谁取到的嘛,枚举一下,比如说是 \(i\) 取到的。为了让 \(i\) 身高为 \(x\),下标为 \(i-1,i-2,...,i-\log x\) 的这些人都可能受到影响,暴力计算即可。得到一个 \(nq\log^2 V\) 的做法。

但是这个题是多个本质相同的问题,没想法吗?改变主体,那就是以最终取到最值的位置 \(i\) 为主体,求出它对不同 \(x\) 的贡献,这个可以从函数去考虑嘛。就是说对一个 \(i\),以 \(x\) 为横坐标,以所需披萨数为纵坐标,画出函数。这个函数显然是 \(O(\log V)\) 段一次函数拼成的,而因为枚举了所有 \(i\),故共有一次函数 \(O(n\log V)\) 段。接下来比较简单,就不提了。

T-060 CF2152H2 S

先想已知 \(x\) 的话,如何计算 \(f(x)\)。从结果去考虑,结果是一个红/黄的染色方法,因为跟答案计算有关,只关注红色的点集。我现在要干什么?要得到一个对于染色方案的限制,使得只有满足这种限制的方案才可能为最优方案。

假设现在已经有一个染色方案(不一定最优),可以用调整法处理,据此能看出最优红色点集肯定是一个连通块。现在还想得到对染色方案的其它限制,但是总体限制已经得不到了,可以去尝试得到单点限制,那就枚举点集内任一点 \(u\)。用调整法,让初态红点集只有这点,最终一定能够把它调整成“最优红点集”。要调整就只能在它周围选出个连通块加进去。删去这个点之后,原图分裂成几个连通块,每个连通块的调整方案没有关系,拆开看。调整嘛,答案要变好。假设有一条边 \(u\to v\),权为 \(w\)。关注删 \(u\)\(v\) 所在连通块的调整,这个调整必然是“加入某包含 \(v\) 的连通块 \(S\) 到红点集”。设恰有一个点在 \(S\) 内的边权和为 \(W\)\(S\) 点权和为 \(X\),那就要求 \(w>W+X\)。因为你发现这个单点限制跟点 \(u\) 没有关系,却跟 \(u\to v\) 有关系,可以改成从为主体描述限制。最优红点集内任一边 \(e\),它把红点集分裂成两个连通块,这两个连通块的“权值”(含点/边权)要比 \(w_e\) 小。这个限制涉及到 \(x_i\)\(w_i\)。但是 \(x\) 是要求的,限制跟它有关就几乎没有用了。能不能只是 \(w\) 内部限制?再进一步,能不能不要“加和”这种东西?

可以,放缩一下限制,得到对点集内任一边 \(e\),以及任一“恰一点在点集内”的边 \(e'\),有 \(w_e>w_{e'}\)。可依值从大到小扫描边,类 kruskal 建立并查集,则可能最优的红点集必然是这个过程中的某个并查集之一。

这是 kruskal 呀,还可以去想重构树,可能最优的红点集就是重构树某子树内所有叶子节点,可以用每个重构树内非叶子节点代表一种方案,后面用“非叶节点的代价”来表述“一种可能最优红点集的代价”。一个这种代价,就是子树中叶子 \(x_i\) 之和,加上叶子 \(u\) 连到子树外 \(v\) 的这种边的边权和。

这是本质相同的一类问题,改变主体,以边 \(u\to v\) 为主体。在重构树上,所有 \(u,v\) 路径上的点(除了 lca 以外)的代价,都要加上 \(w_{u,v}\)。树上差分处理,可以让每个非叶节点赋上点权 \(a\),代表相关边权和。则 \(u\) 节点的总代价就是 \(t_u=a_u+\sum_{v\in \mathrm{subtree}_u}x_v\)

现在已经能够解决“已知 \(x\)”的问题,回到原题,有 \(\min t_u\ge l\) 的限制。变成全局限制,就相当于 \(\forall u,t_u\ge l\)。可以 dp,设状态 \(f_u\) 表示在重构树 \(u\) 子树内,为了所有 \(t_v\ge l\),子树内 \(x\) 和最少为多少。为了解决多组询问,以 \(f\) 为主体,关注 \(f\)\(l\) 变化的函数,发现它是凸的。slope trick 即可。

T-061 ARC201E C

本来是极其简单的题目,用这个题目,把对本质相同问题的处理方法,复习一下。

首先可以改变贡献主体嘛,那就是关注每一个方块 \((x,y)\),问有多少矩形包含它,可写一下对这种矩形的限制,但限制有点复杂。求合法矩形的数目,是计数呀!可以容斥,经过转化,相当于求 \(2^{f(x,y)}\)\(f(x,y)\) 表示在矩形 \((1\to x,1\to y)\) 中有多少原题给出的点。目的是求 \(\sum_{x,y}2^{f(x,y)}\)

这又是一组本质相同的问题。它有点类似于二维数点的形式,可以从小到大扫描一维 \(x\)。我讲一件事,你在很多需要“扫描右端点”的题目中都见过,但可能没有分析。这些题目是扫描右端点,用线段树维护“左端点分别在 \(1,2,...,r-1\)”的答案。这其实利用了递推同时求多个值。当 \(r++\) 时,在已知“左端点分别在 \(1,2,...,r-1\),右端点为 \(r\)”的答案的基础上,对某些位置的答案统一做一点小修改,就递推得“左端点分别在 \(1,2,...,r\),右端点为 \(r+1\)”的答案。

这里也一样的,既然已经扫描一维 \(x\),不妨用线段树维护“第一维是当前扫描的值,第二维分别是 \(1,2,...,n\)”的答案。剩下的不提了啊。

T-062 ARC195E S

大量本质相同的问题,尝试递推,设查询 \(u,v\) 的答案是 \(f(u,v)\),有 \(f(u,v)=(u-1)(v-1)(a_u+a_v)+\sum_{i\le u-1}\sum_{j\le v-1}f(i,j)\),但是这样做不下去。为什么呢?你看原题给了一棵,但此做法并没有用到“树”。

那从树上问题的角度考虑,这是问树上路径呀,没想法吗?差分,或者重剖。此处显然是差分,就是说 \(\mathrm{dis}_{u,v}=\mathrm{dep}_u+\mathrm{dep}_v-\mathrm{dep}_{\mathrm{lca}_{u,v}}\)。要求所有情况下的 \(\mathrm{dis}_{u,v}\) 之和,只需求 \(\mathrm{dep}_u,\mathrm{dep}_v,\mathrm{dep}_{\mathrm{lca}_{u,v}}\) 各自在所有情况下的和。这相当于把原有的一组本质相同问题,拆成了三组本质相同问题。我们挨个求。

\(\mathrm{dep}_u\) 的和为 \(g_u\),则用递推思想,显然 \(g_u=(u-1)a_u+\sum_{i\le u-1} g_i\)\(\mathrm{dep}_v\) 也一样。\(\mathrm{dep}_{\mathrm{lca}_{u,v}}\) 怎么做呢?

这个 lca 很关键,你关注关键的东西,就要枚举它,或者枚举它在何处取到。枚举 \(\mathrm{lca}=x\),问题就天然分成两部分(即使是最暴力的求法,也需要分别考虑这两部分),一部分是 \(x\) 的深度,一部分是 \(u,v\) 的 lca 必须要是 \(x\)。第一部分只跟 \(1,2,...,x\) 节点的父亲有关,第二部分只跟 \(x+1,...,n\) 的父亲有关,两部分互相无关,可以拆开。我再给你说慢一点,令 \(\mathrm{dep}_x\) 期望为 \(d_x\)\(\mathrm{lca}_{u,v}=x\) 的概率是 \(p(x,u,v)\),则答案就是 \((n-1)!\sum d_xp(x,u,v)\)。我在这里用概率/期望的语言是因为好叙述,实际上不需要转化,这都是一样的嘛。\(d_x\)\(g_x\) 的求法一样。关键是 \(p(x,u,v)\) 如何求。

\(x\)\(u,v\) 的最近公共祖先,有两个限制,一个是公共祖先,一个是最近。这两个限制比较重要的是第一个嘛,能不能求“只需满足第一个限制”的概率呢?就是用 \(q(x,u,v)\) 表示 \(x\)\(u,v\) 的公共祖先的概率。

这里有一个类单调性:若 \(t\)\(u,v\) 的公共祖先,则 \(t\) 的祖先也是它们的公共祖先。有个典型问题是,求一些元素的 \(\min\)\(x\) 的方案数。它等于“只要求每个元素都 \(\ge x\),具体如何不做限制”的方案数减去“每个元素都 \(\ge x+1\)”的方案数。这里因为有类单调性,也一样。用 q 减去“存在 \(x\) 的一个儿子,它也是 \(u,v\) 的公共祖先”的概率就得到 p,即 \(p(x,u,v)=q(x,u,v)-\sum_{i\ge x+1}q(i,u,v)\)。那 q 如何求呢?

法一,可以用 dp 递推的方式做,转移时枚举当前两个点的祖先,归纳证明 q 跟 \(u,v\) 无关。

法二,这个问题分两步,第一步是求 \(x\)\(u\) 祖先的概率,第二步是求“\(x\)\(u\) 祖先的条件下,\(x\)\(v\) 祖先的概率”,乘起来就行了。

第一步,从根开始关注 \(u\) 的祖先序列,它是单调递增的。重要的只有点 \(x\),如果从反面考虑,\(x\) 不在序列中,就相当于序列中存在相邻两项跨越了 \(x\)。这个问题只跟每个节点和 \(x\)相对关系有关,转 01 序列。将 \(1,2,...,x\) 涂成黑色,找出 \(u\) 的最浅白色祖先,则它的父亲是黑点中随便选的,如果它的父亲不是 \(x\),那 \(u\) 的祖先中就没有 \(x\)。故 \(x\)\(u\) 祖先的概率就是 \(\frac{1}{x}\)

第二步,已经确定 \(u\)\(x\) 的后代,\(u\) 的父亲是谁就不太重要了,\(u\) 能不能也看成黑点?可以,对所有满足 \(u\)\(x\) 后代的树的形态,将 \(u\) 和“\(u\) 的祖先中 \(x\) 的儿子”这两个节点的编号互换。这样得到的新树还是各不相同。在新树上,除了 \(1,2,...,x\) 外,把 \(u\) 也染黑。找出新树上 \(v\) 的最浅白色祖先,它的父亲,是在黑点内随便选的(也可这样理解:将新树中的 \(x,u\) 合并,就是删去节点 \(u\),将其儿子都嫁接到 \(x\) 上。得到新新树,它们互不相同。然后只需求“\(v\) 的祖先是 \(x\) 的概率”,和上面一样。只不过节点 \(x\) 是胖一点的,它的概率权值是 2)。但需要它的父亲不是 \(u\) 就是 \(x\),故条件概率是 \(\frac{2}{x+1}\),二者相乘得到 \(q(x,u,v)=\frac{2}{x(x+1)}\)

法二中,第二步的手法,就是组合意义法推式子中十分常见的处理:改变选取/枚举元素的顺序,同时将每种可能的情形中元素的排列方式都做一个改变(用来支持这个顺序),要求新旧情形存在一一对应。

T-063 ARC186D B

非常非常简单的题目。首先分析结构,合法序列就是对某棵树做 dfs,每当首次访问节点时打印其儿子数,所得的序列。本题是字典序问题,可以枚举最终序列和 \(A\)lcp 长度为 \(i\),这表示最终序列 \(B\) 的前 \(i\) 项和 \(A\) 的前 \(i\) 项相同。再枚举 \(B_{i+1}<A_{i+1}\)。因为树上节点总个数必须为 \(n\),所以 \(\sum_{j\le i+1} B_j\le n-1\),故暴力枚举在时间复杂度上是可行的。

枚举出来的内容,带来了什么限制?放到树上看,就是要求 dfn 为 \(i\) 的节点的儿子个数必须为 \(B_i\)。求方案数,就是问在此基础上补充节点得到总共 \(n\) 个节点的树,共有多少种树。对于已经回溯的节点,不用管它。但是尚未接全儿子的节点很麻烦,每个这种节点都像一个子问题“已知根度数,树大小任意”。

有多棵“子问题”树,可以把它们合并成一棵,通过建立虚根解决。先把尚未接全的儿子补全,再拿虚根向这些儿子连边,转化为求“当前共有 \(i\) 个节点可以随意接儿子拓展,希望最后共有 \(j\) 个节点”的树的个数。这显然是不能依靠树的分治性质去 dp 的,那就得记录全局状态,两维必然分别是上面提到的 \(i,j\)。转移大概就是新加入一个节点,给它选择父亲 \(p_i\)。但是为了去重,还得要求对于结构相同的树,只有 \(p\) 最小的序列被合法计入。

在本题中,这相当于要求 \(p\) 单调不降。可据此写出转移 \(f_{i,j}=f_{i-1,j}+f_{i+1,j-1}\)。第一种转移代表,确定了当前 dfn 最小的节点以后不再接儿子,把它从当前序列中删掉。第二种转移代表,给当前 dfn 最小的节点加上一个儿子。但是这个 dp 状态就炸了,如何优化?

每次转移的步长很小,而且初始化只有一个位置,没想法吗?格路计数。不过不能直接做,可以先下标换元,让 \(f_{i,j}\) 对应的格点坐标为 \((j,i+j)\),则转移就等同于经典格路计数的转移了。注意因为 \(i+j\ge j\),走路的时候必须在直线 \(y=x-1\) 上面,不能碰到它。

T-064 CF543E A

首先有一个简单的主席树做法,版本轴表示当前考虑到质量小于 \(t\) 的所有歌曲,主席树维护 \(c_i\) 表示在 \([i,i+m-1]\) 有多少这种歌曲。每次修改就是令 \([x,x+m-1]\)\(c\) 都加上一;每次询问就是问 \([l_i,r_i]\)\(c\) 的最小值。但是空间要求 \(O(n)\),不能通过。时间轴/版本轴是极其重要的,没有它就做不了,但是你知道的唯一一个带有时间轴的数据结构被卡掉了,那怎么去处理呢?

用通用数据结构分块,分块里面有一个“时间轴/操作序列”分块嘛。那就是说,每 \(O(\sqrt n)\) 个大版本,记录一次信息。每次查询前,需要重新把从最近的版本到当前时刻中间的 \(O(\sqrt n)\) 个小修改进行了。

从查询的角度看,如何处理散块查询?常见的散块查询要求,能够从整块标记和原有块内元素,还原出当前元素。但是空间要求 \(O(n)\),如果记录“原有的每个元素”,空间就达 \(O(n\sqrt n)\),无法承受。一般的方法不能处理,观察本题的特殊性质\(c_i\) 只涉及到 \([i,i+m-1]\) 这个区间,而散块查询要一次性查询 \(c_i,i\in[l,r]\)。没想法吗?

双指针。具体来说,例如查询某个块开头 \(st\) 向后到 \(r\) 这一段的散块值。只需记录 \(c_{st}\),则 \(c_{st+1}\) 可以通过 \(c_{st},a_st,a_{st+m}\) 得到。至于整块查询,看来只能扫描一遍涉及到的 \(O(\sqrt n)\) 个块。

从修改的角度看,每次修改要求时间复杂度 \(O(1)\)。整块修改只需打差分标记,在查询前,做一次前缀和把差分标记处理掉即可。但是散块修改怎么做呢?常见的是按类似于散块查询的方法做,但这里复杂度不能接受。

答案的角度,一次整块查询,只需要所有涉及到的块的最小值。整块修改打标记时,块内最小值就跟着标记增加即可,其取到的位置不变。但是散块修改时,块内最小值取到的位置会变,可是你又不知道块内信息(如果想知道,空间就炸了),那不就做不了了吗?

你想,什么时候知道这些信息?最近的一次,就是在处理最靠近本次查询时刻的大版本时,我们还知道块内具体信息,只是无法记录而已。那我不要具体块内信息,只要一个最关键的答案,看起来需要存储的信息量小了,行不行?换句话说,只需记录从第 \(i\) 个大版本开始,到第 \(j\le O(\sqrt n)\) 次小修改及之前,哪些块的最小值取到位置变化了,以及分别变成了哪些位置。可以这样预处理吗?可以,具体地,当初离线计算第 \(i\) 个大版本的全套信息后,再往后做一次扫描,能够得到做 \(j\) 个小修改后的全套信息。这样,每个小修改,它改变哪 \(O(1)\) 个块的最小值位置,以及改到哪里都可以直接得出,记录一下。每个小修改只记录 \(O(1)\) 的信息,不会炸。

T-065 QOJ12361 D

数据范围加强到 \(|s|\le 10^7\)。极简单一道题。这里有循环节,关注周期/border,对应 \(O(n)\) 算法 KMP。这里有回文,关注 \(O(n)\) 算法 manacher。换句话说,想要用 KMP 数组,以及回文数组,去刻画限制

怎么观察限制?一般就是要枚举几个元素,才好观察。这里可以假设现在打暴力,已经枚举了 \(l,r\),问 \([l,r]\) 是否合法,怎么去做呢?因为 \(ww^r\) 是周期,则 \(2(r-l+1)\) 必须是某个周期的长度。求所有可能周期长度,可以通过 border 求,就不复习了。只满足这一点,只能保证 \([l,2r-l+1]\) 是原字符串的周期,为了合法,\([l,2r-l+1]\) 还得是回文,大概只需 manacher 数组 \(p_r\ge (r-l+1)\) 即可。边界情况自行讨论。限制摆出来了,枚举右端点,就能上数据结构维护,不提了啊。

T-066 P14211 B

首先分析结构,这个跟分析限制一样的,要先枚举一两个元素。我要分析什么结构?从答案来看,是两个路径的交。那假设现在枚举了两条原有路径,如何刻画它们的交?

这个交也是个路径,要求的是路径长度,最常见的就是用 \(\mathrm{dep}_u+\mathrm{dep}_v-2\mathrm{dep_{lca}}\)。自然地,关心交路径的两个端点和“自身 lca”。那这里就分两种情况,一种是交路径的“自身 lca”和它的某端点重合,这个重合的位置显然是某原路径的“自身 lca”。两条原路径“自身 lca”呈祖先/子孙关系时,才会出现此情形。另一种是,两条原路径“自身 lca”重合时,交路径的“自身 lca”也与之重合,但是交路径“自身 lca”与交路径端点没有重合。分别计算。

对于第一种情况,关键的位置是两条原路径“自身 lca”中,较深的那一个(因为它就是交路径的一端点和 lca),在此处统计答案。那就要枚举那个较深“自身 lca”位置 \(x\)。需要关注所有“自身 lca”等于 \(x\) 的原路径,可以把原路径挂到自身 lca 处。这样做以后,只需考察所有挂到 \(x\) 的原路径,让它们和所有“有一端点在 \(x\) 子树内,仍在向 \(x\) 父亲延伸的原路径”求最大交。枚举所有挂到 \(x\) 的原路径 \(l\),它和“仍延伸”路径的交何时最大?这个交就是 \(\mathrm{dep_{lca}}-\mathrm{dep}_x\),这里的 lca 就是“仍延伸”路径在本子树内的端点以及 \(l\) 某一端点的 lca。希望两点 lca 深度尽量大,没想法吗?

这就是想要两点 dfn 尽量连续嘛,只需维护所有相对于本位置的“仍延伸”路径在本子树内的端点,枚举 \(l\) 后,再枚举 \(l\) 的一端点,在所有“仍延伸”端点中查找 dfn 和它最近的两个,算 lca 即可。至于维护“仍延伸”路径端点,启发式合并即可。

对于第二种情况,我们一般还是想套用已经想到的东西。已经想到的有两点,一点是枚举关键位置并在此处统计答案,一点是对“仍延伸”路径用启发式合并。关键的位置依然是交路径的某一端点,这个端点是两条原路径各取一端点,做 lca 产生的。枚举此端点 \(x\)。用“仍延伸”的语言描述,两条原路径也就是相对于 \(x\) 的,来自不同 \(x\) 子树的“仍延伸”路径,这两个“仍延伸”路径的“自身 lca”还得重合(满足第二种情况的基本限制)。这个在启发式合并的过程中,就能维护,跟上面的差不多。就不提了啊。

T-067 AGC074B 25-10-27 B+

其实非常非常简单的。首先从势能去分析,\(\sum a_i\) 是不变的。那可以把原序列看成 1 的移动,只需关注 1 即可。这种要进行序列重排序的构造题目,比较常见的结论是“仅小操作有用”,在这里不妨假设只有 01 和 10 互换的操作是有用的。要关心的元素只有 1,那不仅要关心其单点性质例如位置等,还需关注双元相对性质。随便拿出两个 1 来,它们的相对性质只能是跟位置有关的(若不关心位置,便无其它可关心者)。自然地,考虑两个 1 的位置相对前后关系,发现它始终不变。进一步地,再关心 1 的整体性质,还得跟位置 \(p_i\) 有关,最简单的莫过于考察势能量 \(\sum p_i\),它也是恒定的。基本可以据此判断无解了。

由以上诸多性质,现在可以去关注解是什么样的。题目给出的信息包含 1 的初始和结束位置,因相对位置不变,故原有第 \(i\) 个 1 在这个解中,必定移到了最终的第 \(i\) 个 1 处,可以求出每个 1 要向左/向右移动多少。之后,对其模拟比较容易,但是步数超限,是 \(O(n^2)\) 的,因为“仅小操作有用”其实是假结论。

修改本做法,先从头到尾检查一遍。\(\sum p_i\) 恒定判无解还是没有问题的。本做法的核心基于 1 相对位置不变,即使结论假了,也强制其不变行不行(对原算法做尽量小的修改)?那就是说每个 1 向左/向右应该移动的距离还是和原来一样,只加速 1 的移动速度。先考虑最简单的,也就是单点加速,去做形如 100...00,00...001 的对换。可以每次移动在边界上的一个 1,做到 \(O(n)\),已经很接近了,但单点加速已经没有前途了,怎么处理呢?

有没有发现最开始的一个前提是不坚实的,为什么要关注 1?能不能关注 0?0 和 1 是对称的,二者中最少的一个,最多只占 \(\frac{n}{2}\)。就做完了。

T-068 AGC074D 25-10-27 S

分析题目结构。比较重要的在于合并的过程,是一个类 kruskal 的过程,不妨假设知道了最终的 kruskal 重构树的形态。在此基础上,考虑什么样的序列是“好序列”,可以关心如何判定一个序列好不好。

一次询问 \((i,j)\) 是否连通,只涉及到 \(i,j\) kruskal lca 的合并时刻,以及询问时刻的关系。换句话说,以非叶子节点 \(x\) 为主体,设 \(x\) 两个儿子的实际子树大小(对应“并查集连通块”大小)之积为 \(t\),则涉及到 \(x\) 的询问就有 \(t\) 个。判定的过程就是,依次为每个询问指定一个对应非叶子节点,使得 01 信息不矛盾。把限制写出来,假设为每个节点维护一个 01 序列,表示当前为止,所有依时间顺序已指定到本点的询问的 01 状况。则若某个节点已经填写 1,它就不能再填写 0,它的后代也不能填 0。那就是说,为了 01 信息不矛盾,所有的限制都是从上向下的(具有明显顺序,看来可以贪心使之满足,无需复杂图论结构如拓扑排序等),且只有填写 1 之后才会出现。

对于已知的树结构和待判 01 序列,可贪心选择一个最优方案,也就是限制最少的方案,据此方案执行,来看行不行。此贪心方案就是让 1 尽量在下面、0 尽量在上面。准确地,所有填过 0 的位置构成一个包含零的连通块。这个连通块很关键,但它还不太完美。

因为连通块边界处可能还有填 1 的。能不能减少这种“混合节点”?假设已有一个合法连通块及对应填写方案,可只对其边界节点进行调整,使 1 更集中,最终使得混合节点仅剩一个。换句话说,一个 01 序列在某树结构下合法,当且仅当,存在一种填写方案,使得所有 0 产生含根连通块,且仅有一个在边界上的混合节点。

现在这个方案的树性已经变弱了,能不能换一种方式来描述它,减少描述中的无效信息量呢?也就是说,先看看合法性究竟和连通块的哪些东西有关,找出重要的信息,通过这些信息不重不漏地考察所有连通块的形态,而不是从所有树的形态生成出所有连通块。求出连通块所有节点 \(t\) 之和 \(T\),以及混合节点 \(t\)\(t_m\),记录 0 个数共计 \(o\) 个,若要合法,则 \(T-t_m<o\le T\)。另外,第 \(o\) 个 0 后面,至少要有 \(T-o\) 个 1。关键信息就是 \(T,t_m\),它们是由“每个叶子节点分别在哪个子树中”生成而来。要考察所有可能的 \(T,t_m\),只需讨论每个叶子节点是连通块中哪个最深节点的后代。

改为以连通块节点为主体。将连通块往下拓展一圈,将新拓展节点依次标号,尤其是将混合节点的两个儿子标为 \(1,2\)。记录 \(b_i\) 表示以新标号 \(i\) 为最浅的非连通块内祖先的叶子有多少。则 \(\sum b_i=n,\sum_{i<j} b_ib_j=T,b_1b_2=t_m\)。因为 \(T\) 在满足大于 \(o\) 的前提下越小越好,还需要满足 \(T-t_m<0\),可以去做 dp。稍微想一下就知道应该把 \(b_1,b_2\) 放到最后确定。定义状态 \(f_{i,j,k}\) 表示填了前 \(i\)\(b\)(不是 \(b_1\to b_i\),是 \(b_3\to b_{i+2}\)),\(\sum b_i=j,\sum b_ib_j=k\) 是否可行。得到一个 \(O(n^5)\) 的做法,但无法优化,主要是因为合法条件要连判两次,一次是确定 \(b_1,b_2\) 之前,一次是之后。

想要简化 dp,只需将两个合法条件分开判,把关键元素 \(b_1,b_2\) 和其它元素拆开。那就是 \(O(n^3)\) 枚举 \(b_1+b_2=A,\sum_{i<j,i\neq 1,2} b_ib_j=B\),则 \(B+A(n-A)<o\le B+A(n-A)+b_1b_2\) 就合法。最右边(即 \(T\))可以通过调节 \(b_1,b_2\) 得到最小符合限制的。\(B\) 能不能合法,还得通过上面的 dp 预处理求得。数组内只有 01 呀!可以换维,注意到第三维若较大的 \(k\) 成立则较小的也成立,改变状态为 \(f_{i,j}\) 表示填了前 \(i\)\(b\)\(\sum b_i=j\)\(\sum b_ib_j\) 最大是多少,即可。

T-070 P11831 25-10-29 B+ 折戟沉沙铁未销,自将磨洗认前朝。

首先这是一个 DAG 上的连通性问题,可以 \(O(\frac{n^2}{\omega})\) 预处理一个节点的所有后代。先看没有修改怎么做。

查询涉及到两个限制,一个是 \(a_y\in[l,r]\),一个是 \(x\to y\)。为了处理第一个限制,可以对 \(a\) 分块,设块长 \(b\)。查询时,依据预处理的 \(x\) 后代的 bitset,散块暴力总复杂度 \(O(qb)\)。那整块如何查询呢?一次查询中,对于一个整块最好只涉及到 \(O(1)\) 个元素。这是分块题目,那就要预处理。最简单的方式就是预处理 \(c_{k,x}=\max_{a_y\in[kb,kb+b],x\to y}b_y\)

这是一类本质相同问题,转化贡献主体,那就是关注一个 \(y\),它的 \(b_y\) 赋值给了哪些 \(c_{k,x}\)。式子里面有 \(\max\),故从大到小扫描 \(b_y\),利用 \(y\) 的祖先 bitset,以及 \(x\) 只被覆盖一次的性质,可以做到总复杂度 \(O(\frac{n^2}{\omega})\) 预处理。故无修改问题的总复杂度可以做到 \(O(\frac{n^2}{\omega}+q(b+\frac{n}{b}))\)。加上修改,其实两个修改几乎相同,若仍以上面做法为基础,两个修改都必须重构修改点所在块,总修改复杂度 \(O(\frac{qbn}{\omega})\),而初始预处理、查询复杂度不变。得到总复杂度 \(O(\frac{n^2}{\omega}+q(b+\frac{n}{b})+\frac{qbn}{\omega})\) 的做法,对 \(b\) 求导计算平衡复杂度,\(b=\sqrt{\omega}\) 最优,也就是说,最优总复杂度 \(O(\frac{n^2}{8})\),完全没用。

归根结底是修改重构复杂度过高无法接受。可尝试优化一下,但无论如何每次修改都必须重构,这意味着单次修改复杂度至少 \(O(n)\),不能接受。只能是别修改,如何规避修改?时间轴操作,这里就是时间轴分块。每 \(B\) 个查询/修改重新预处理一次,总预处理复杂度 \(O(B\frac{n^2}{\omega})\),那 \(B\) 只能是 \(O(1)\) 的呀!要不然仅预处理就无法接受了。这表示,预处理复杂度太高。

回去看预处理,本质相同式子还有什么办法?递推\(c_{k,x}\) 可以 \(O(n)\) DAG 上 dp 求得呀!总预处理复杂度降低为 \(O(\frac{n^2q}{Bb})\)。那查询时,还需考虑最新的 \(O(B)\) 个修改,一次修改相当于把某位置的元素删除,再添加另一个元素到该位置。“添加”好做,但无法“删除”,因为整块的预处理信息不支持删除。

删不了就别删,怎么不删?你想想,时光轴操作本就是为规避删除操作设计的,肯定是利用时光轴分块规避删除。你想想时光倒流把连通块分裂变成合并的做法,它从“删无可删”的状态开始倒流的。这里也可以处理到“删无可删”,也就是预处理当前版本时,提前将之后的 \(O(B)\) 个修改涉及到的“删除”位置都删了。那每次查询,只需“添加”\(O(B)\) 个元素即可,总复杂度 \(O(\frac{n^2}{\omega}+\frac{n^2q}{Bb}+q(b+\frac{n}{b}+B))\),复杂度平衡时 \(B=b=n^{\frac{2}{3}}\),总复杂度 \(O(\frac{n^2}{\omega}+n^{\frac{5}{3}})\)

T-071 P9531 25-11-5 S

这是一组本质相同的问题,先考虑如何简化单个问题的计算方式,再尝试递推计算或者改变贡献主体。对于单个问题,若所需铁轨宽度为 \(x\),则相当于将边权设为 \(|w_i-x|\) 然后跑最小生成树。绝对值函数呀!依据 \(w_i,x\) 的大小进行分讨。那就是说,将边按 \(w\) 排序,分别考察 \(x\) 左边、右边的所有边,这两部分边关系不大,可拆开分别处理。

拆开有什么用?各自很容易排序了。排序有什么用?可建立 \(x\) 左侧所有边构成的最大生成树、\(x\) 右边所有边构成的最小生成树,只有在这两棵树上共计 \(O(n)\) 条边是有意义的。找出这 \(O(n)\) 条边后,再把它们依 \(|w_i-x|\) 排序,重做最小生成树。那如何找出 \(x\) 左侧、右侧的树边呢?递推。以左侧最大生成树为例,从 \(x-1\) 的树推到 \(x\) 的,只需加入 \(w_i=x\) 的边 \((u,v)\),这个边的优先级是最高的,一定会加入树边集合。在原有生成树上做调整,把 \(u,v\) 之间简单路径的最短边删去即可。得到一个 \(O(nq\alpha(n))\) 的做法,但难以进一步优化,因为最终的“重做生成树”是不能接受的。

那尝试转换主体,以边为主体,观察它对不同 \(x\) 的贡献。什么叫贡献?最简单的,在最终生成树上叫做有贡献。\(i\) 能够贡献的 \(x\) 是一段区间。更明确一点,边 \(i\) 存在于 \(x\) 对应的最终生成树 \(T(x)\) 上,当且仅当 \(x\in (l_i,r_i]\)。转化为求 \(i\) 这条边对应区间的左右端点。这是对称的问题,只需会求左端点。从结果去考虑,假设知道结果,观察性质。若知道 \(l_i\)(是个实数),能观察什么性质?你关注的东西得跟 \(l_i,i\) 都有关。\(l_i\) 相关东西只有一个,就是 \(T(l_i)\)\(T(l_i)\) 上“可以有边 \(i\),也可以没有”。换句话说,\(T(l_i)\) 上,\(\max_{j \in u_i\to v_i} |w_j-x|=|w_i-x|\)

存在限制转全局,这要求 \(\forall j \in u_i\to v_i,w_j\in [2l_i-w_i,w_i]\)。稍微想一下,这等价于只用 \(w_j\in [2l_i-w_i,w_i]\) 的边就能将 \(u_i,v_i\) 连通。跟 T-069 一样,此限制并不充要,还需要 \(\exists j \in u_i\to v_i,w_j=2l_i-w_i\)。那么只需找出最大的 \(w_j\) 使得 \(w_k\in [w_j,w_i]\) 的边能将 \(u_i,v_i\) 连通,则 \(l_i=\frac{w_j+w_i}{2}\)。那如何求 \(w_j\) 呢?

利用所有 \(w_k<w_i\) 的边,以 \(w_k\) 为边权建立最大瓶颈生成树,则最大瓶颈生成树上 \(u_i,v_i\) 路径上的最小边权值就是 \(w_j\)。这个最大瓶颈生成树,可以用类似于上面部分分做法的方式,逐步调整得到。求得 \(l_i,r_i\) 之后,算贡献非常简单,就不提了。

T-072 P10627 25-11-7 A

结果去考虑。什么叫“从结果”?用一些信息去描述答案取到时的方案。注意,这些信息(即某些元素的取值)的所有组合方式,必须能够囊括所有可能的方案。描述方法只对统计答案服务(依据这种描述能计算答案是多少即可),故其所有可能性,可以包含不合法的方案、不优的方案。为了让方案合法优秀,可以在元素之间加上限制,具体是哪些限制,可以从调整法等入手分析。如果难以分析限制,也可思考如何用贪心/dp 判断方案合法性。

对于这道题,一个最简单的想法就是用“每个人是送到左边/右边的医院/不送”来描述答案。此描述法显然包含了大量不合法方案,为了保证合法,最基础的限制就是“依时间排序同一条边上的人,不送医院的是一段后缀”。可改变描述法,对每条边 \(i\) 记录一个 01 字符串 \(s_i\) 表示依时间顺序每个人是送左边/右边的医院,另记录 \(k_i\) 表示这条边上送医院的总人数。但不合法的还是很多。

你想,限制从哪里来?肯定是从医院上来。因医院只对病人总数做限制,对我们合法方案的限制,看来也只能是以数量关系出发的总体限制,而不是具体到每个病人 01 选择的单点限制。具体来说,设 \(l_i\) 表示第 \(i\) 条边字符串上 0 个数,\(r_i\) 表示 1 个数,\(n_i\) 表示病人总数,则若 \(k_i\neq n_i\),有 \(l_i+r_{i-1}=c_i,r_i+l_{i+1}=c_{i+1}\)。另外,如果有人不去医院,在他中暑之前,他两边的医院必须满员。刻画满员条件的方法就是关注所有到达该医院的人,中暑最晚的时刻。设第 \(i\) 边上送入左医院的最晚中暑时刻为 \(L_i\),送入右医院的是 \(R_i\);第 \(i\) 边上第 \(j\) 个人的中暑时刻为 \(t_{i,j}\),则 \(t_{i,k_i+1}>\max(L_i,R_{i-1}),t_{i,k_i+1}>\max(R_i,L_{i+1})\)。接下来开始做 dp 及其优化,不是重点,就不提了。

T-073 P11677 25-11-13 A

结果去考虑,描述一个方案的办法是,记录每个操作的位置,构成一个可重集,答案就是集合的大小。最优方案的可能性太多,又因数据范围无法直接 dp,也无法用图论方法去做限制。

可以用调整法减小方案可能性。假设已知一种合法方案(未必最优)。一次正确调整,有两个限制。第一个是要保持方案合法性,第二个是操作集合大小不能变大。从第二个限制入手,先不奢求“操作集合变小”,先让“操作集合大小不变”。这样,一次调整可以看成“删去若干元素并添加等量的若干元素”。但方案合法性较难判断,因为若有任一位置,调整后的总受力变小了,就很难知道它是否仍旧合法(若非要知道,将不得不利用原有操作集合,和 \(p_i\) 去判断,那你的调整方法就失去了一般性,不好)。

因此,只需让每个位置调整后总受力都不变小,那合法性必然保持!现在关心调整时,删除几个元素。先考虑删除一个,再添一个。不可能保证每个位置调整后总受力都不变小。若删两个,设删除了 \(x,y(x\le y)\) 并添加 \(x',y'(x'\le y')\),根据“总受力的减小量”和“总受力的增加量”去做分类讨论,可得到 \(x'=x-1,y'=y+1\) 是正确调整。据此,一定有一种最优解满足:只操作若干次 1 位置和若干次 \(n\) 位置,最多操作一次中间某个位置。假设左右两端各操作 \(x,y\) 次,中间操作 \(m\) 位置,则 \(ix+(n-i)y+|m-i|\ge a_i\)。枚举 \(m\),只需求 \(x+y\) 的最小值,可二分 \(x+y=t\),这要求 \((2i-n)x+(n-i)t+|m-i|\ge a_i\)。它们是对 \(x\) 取值范围的限制,它们有交集则判定可行。得到 \(O(n^2\log V)\) 的解法。

现在肯定是,从左向右扫描 \(m\),对一些限制进行微调。计算当前 \(m\) 时,因限制和 \(m-1\) 时的类似,想要据此削减 chk 函数的复杂度。但 \(t\) 也在限制里,很难维护一次微调对所有 \(t\) 的影响。你想,\(m\) 对答案其实没多少贡献,不同 \(m\) 二分出的答案 \(t\) 之差其实不超过一的(假设不操作 \(m\),改为各额外操作一次 \(1,n\),只增加一次操作,合法性不变)。先求出只操作 \(1,n\) 时的答案 \(k\)。只需扫描 \(m\) 并判断 \(t=k-1\) 是否可行。此时,每个位置 \(i\)\(x\) 的限制形如 \(x\le \frac{a_i+|m-i|}{2i-n}\)(或者 \(x\ge ...\)),由调和级数,这种限制合计随 \(m\) 变化 \(O(n\log n)\) 次。动态维护一下最紧的两个限制即可。

T-074 ARC209C 25-11-14 A+

如果只有一个询问,可定义状态 \(f_{i,j}\) 表示现在在位置 \(i\)\(i-1\) 位置能更改的那一维,被改为了 \(j\),在 \(i\) 及之前选择的最大价值是多少。现从结果去分析,一个方案可用“每个 \(i\) 处的条件 \(ab\ge s_i\) 是否满足”来描述。换一种对统计答案更友好的描述方式(不换也可,只多了一些分讨),若 \([p_i=1]\oplus[ab<s_i]=1\) 记作 \(c_i=1\),反之记 \(c_i=0\),则答案只和字符串 \(c\) 中 1 的个数有关。那如何判断方案合法性?

如果一个元素 \(c_i=1\),则它需要在满足自身条件的基础上尽量为下一个元素服务(若 \(c_{i+1}=0\),则不用服务)。\(c_i=0\) 则只需为下一元素服务即可,实际上这些 \(i\) 位置能够更改的维度,可以变成下一元素想要的任意值。更进一步,此时,因 \(i-1\) 无需为 \(i\) 服务,则 \(i\) 不能改的维度(\(i-1\) 能改)也可变成 \(i+1\) 想要的任意值。

现在你看 \(c_i=0,c_i=1\) 的元素谁更关键?等于零的,因为若把它们当框架,框架间填充了“一”连续段,连续段间无关。判断方案的合法性,只需判断所有“一”连续段是否合法,可以用 dp 去预处理,定义状态 \(f_{i,j,k}\) 表示“一”连续段从 \(i\) 延伸到当前位置 \(j\)\(j-1\) 能更改的那一维被改为 \(k\)\([i,j]\) 区间能否合法。

观察转移,发现对于确定的 \(j\)\(k\) 有用的仅 \(O(\sqrt s)\) 种。答案值域极小的 01,又具备单调性,可换维,更改状态为 \(f_{i,k}\) 表示“一”连续段从 \(i\) 开始延伸,\(i-1\) 能更改的那一维被改为 \(k\),合法的 \([i,j]\) 区间对应的最大 \(j\) 是多少。其预处理复杂度已可以接受。其实它还满足区间包含单调性,故对于一次询问,因为想要“零”位置尽量少,只需每次找出当前位置延伸出的,合法“一”区间的最远右端点,将该位置加一选做“零”位置,然后跳到“零”位置加一处。处理上述过程,倍增即可。

T-075 CF2006D 25-11-17 C

先学会判定一个序列是否合法。可以猜出最优的重排方式是,最大值,最小值,次大值,次小值,等等。假如将原有序列排序后重标号,则只需满足形如 \(a_ia_{n-i+1}\le k\) 的限制,则合法。这是个乘积呀,没想法吗?

\(a_i,a_{n-i+1}\) 必有一个小于等于 \(\sqrt k\)。你不觉得 \(\sqrt k=10^3\)吗?不妨要求 \(i\le n-i+1\),限制不会变松。可以枚举 \(a_i=t\le \sqrt k\)。此时如何判定 \(a_{n-i+1}\le \frac{k}{t}\) 呢?

关注 \(\frac{k}{t}\),它只有 \(O(\sqrt k)\) 种,把 \(a\) 序列按值域分成了那么多段,只关心每一段的最小标号。换句话说,我们对限制做了一个下标-值域对称。记录 \(c_i\) 表示原序列大于等于 \(i\) 的元素个数,则 \(c_{\frac{k}{t}}<n-c_t\)。那如何更改元素最优呢?不管是哪个 \(t\) 对应的限制,都是每次把最大的元素改成一最好嘛。

接下来随便做了,可以莫队,也可以把不同的 \(t\) 分开算答案,最终答案取 \(\max\)

T-076 P14344 25-11-17 B

首先有一个 \(O(n^2)\) 的 dp 状态 \(f_{i,j}\),表示当前的最后一步是去做了 A 菜肴的第 \(i\) 步,在此之前共做了 B 菜肴的 \(j\) 步,最大价值是多少。每次艺术评分形如“若 A 菜肴制作第 \(i\) 步前 B 制作了不超过 \(a_i\) 步,则得分 \(p_i\)”和“若 B 菜肴制作第 \(i\) 步前 A 制作了不超过 \(b_i\) 步,则得分 \(q_i\)”。

在这个状态下,第二个艺术评分不太好计算,可做一个对称“在 A 菜肴制作第 \(b_i\) 步前 B 制作了不少于 \(i\) 步,则得分 \(q_i\)”。接下来如果你愿意再做一次对称,还能把“不少于”也变成“不超过”,方法就是提前令答案加上所有 \(q_i\),然后“在 A 菜肴制作第 \(b_i\) 步前 B 制作了不超过 \(i\) 步,则得分 \(-q_i\)”。其实也可不做这个对称。

接下来优化 dp,关键的决策点只有共计 \(O(n+m)\) 个形如 \(f_{i,a_i},f_{b_i,i}\) 的位置。直接在两个决策点之间转移是做不了的,但是如果扫描第一维并使用线段树维护数组,只为关键点做转移,就能解决。

T-077 25-11-18 S

\(n\le 5\times 10^5\) 个桶,第 \(i\) 个桶容量为 \(i\),初始时水量 \(a_i(\sum a_i=n)\)。可以让桶 \(x\)\(y\) 倒水,一次倒水必须要么把 \(x\) 倒空要么把 \(y\) 倒满。问如何倒水,使第 \(i\) 个桶最终水量为 \(b_i(\sum b_i=n)\),要求操作次数小于等于 \(k\) 步,构造方案。有四档部分分,全部通过才算作本题通过。分别是 \(b_i=1,k=2n\)\(k=2n\)\(b_i=1,k=\frac{4}{3}n\)\(k=\frac{5}{3}n\)

第一档,可以先把所有水集中到第 \(n\) 个桶,然后依次操作 \(n\to n-1,n-1\to n-2\),等等(做法一)。这启示我们,可以先把所有水集中到 \(n\)

接下来关注到 \(\frac{4}{3},\frac{5}{3}\),它们在构造题一般就标志着分组构造或者拼合做法。什么叫分组构造?“用 4 步解决某 3 个桶的需求”。先尝试部分分二,“用 2 步解决 1 个桶的需求”。

如果已提前把所有水集中到 \(n\),就可以用两步解决一个桶:一步是划出那个桶要的水,另一步是把这些水运到对应桶中。具体来说,倒序依次解决每个桶,假设当前待解决桶是 \(i\),当前水(未被解决的桶需要的所有水都放在该桶中,初始化条件下 \(x=n\))在桶 \(x\),且 \(x\) 中有 \(p\) 滴水(初始化 \(p=n\))。则只需 \(x\to p-b_i\) 就能让 \(x\) 残留 \(b_i\) 水,再做 \(x\to i\),之后令 \(x\) 赋值成 \(p-b_i\) 即可(实际上任意时刻 \(p=x\))。但这个做法有问题,如果 \(p-b_i>i\) 就完蛋了,你会毁了已经解决的桶!怎么办呢?

我一再强调你要想周全一点嘛,如何变得周全呢?可以给不同的主体应用同一个思想,也可以做对称嘛。刚才是把 \(b_i\) 残留在 \(x\) 呀,那能不能让 \(p-b_i\)\(x\)\(b_i\) 在另一处呢?可以。我再给你从头说一遍,倒序解决,当前待解决桶是 \(i\),当前水在桶 \(x\) 且有 \(p\) 滴水。只需 \(x\to b_i\) 就能让 \(b_i\) 滴水放在第 \(b_i\) 号桶中,再做 \(b_i\to i\) 即可(实际上 \(x\) 恒等于 \(n\))。因为 \(b_i<i\),故规避了上述问题,可行(做法二)。但最坏 \(O(3n)\) 次操作,在本题中(似乎)没任何用,且优化该做法的尝试都失败了。

你重新看,\(\sum a_i=n\) 很奇怪,那能不能做一个关注值域的对称,考虑“用 ... 步解决 ... 水量的需求”?也就是说,若 \(a_i>b_i\),则桶 \(i\) 应该运出 \(a_i-b_i\) 滴水;反之,应该运进 \(b_i-a_i\) 滴水。总计 \(\sum |a_i-b_i|\le 2n\) 滴运进运出要求。对于部分分二,只需能够用 \(2n\) 步解决 \(2n\) 滴需求即可。进一步地,可以分组,要求用 2 步解决 2 滴需求。每次找出一个仍需运出的桶 \(x\) 和一个需要运进的桶 \(y\),操作 \(x\to 1,1\to y\) 即可,可通过第二档(做法三)。

如果还是想要只使用分组去处理整个题目,就得“用 4 步解决 6 滴需求”,做不了。那拼合做法平衡复杂度能行吗?可以,对于部分分三,其实可以把做法一、三拼一下。我多次强调,拼做法之前要先严格分析复杂度。记录 \(a_i\neq 0\) 的个数为 \(A\),则做法一严格操作次数 \(O(A+(n-1))\);做法三严格次数 \(O(\sum |a_i-b_i|)\),我们想要利用 \(A\) 去描述它,可以转化成 \(O(2(n-A))\)。平衡复杂度(二者选择步数少的执行),得到最多次数 \(O(\frac{4}{3}n),A=\frac{3}{2}n\),可以通过第三档。

现关注最后一档,这一档平衡复杂度要用到的变量肯定有 \(B\)(定义类似)。据此分析做法二的复杂度,是 \(O(A+2B)\)。试图更改做法三使之能够用 \(B\) 描述,可是这个绝对值很麻烦,而且跟 \(B\) 没关系。

你想,做法三在第三档的复杂度分析时,之所以能够用 \(A\) 描述,就是因为它是从 \(a_i\to 1\) 的;对称地,也可以用 \(B\) 描述 \(1\to b_i\)。现在想要 \(a_i\to b_i\),可拆成 \(a_i\to 1\to b_i\)。具体来说,把做法三改成“现用原有做法把它摊平,使得每个桶中都是 1 的水,由此状态继续做运进运出得到 \(b_i\)”,复杂度 \(O(2(n-A)+(n-B))\)。做法一的更改也呼之欲出,也是“先用原有做法摊平”,然后做“运进运出”,复杂度 \(O(A+(n-1)+(n-B))\)。用这三个做法平衡复杂度,就可以通过了。

T-078 P12033 25-11-20 A

如果本题是 dp 去做的,那肯定要运用线段树维护矩阵/矩阵快速幂嘛。先思考奶牛和草的总数小于等于 \(2\times 10^5\) 怎么做呢?任取一头牛为主体,它所吃的草是一个区间。这个区间必然包含牛的原位,且以这个原位为分界点,其一边,牛经过了两次,走过去再走回来;另一边只经过一次。而且,任两头牛的区间不会相交。想要描述一个方案,只需描述每头牛的区间即可。但使用这个描述法得到的 dp,其状态比较复杂,不符合矩阵 dp 的要求。那要更换描述法,而描述法几乎只跟选取的主体有关,就要更换主体(实际思路过程应该是,考虑到主体有很多,先根据每个主体想出一种描述法,再用最有前途的描述法继续做)。

以草为主体,和以牛为主体是几乎对称的,没用。那以谁为主体呢?描述法一定要能统计答案,那么思考统计答案有哪些方式。可以拆贡献,以(把数轴当成一个链,牛的移动看成沿边行进)为主体来统计。也就是说,只需描述每条边被经过 0/1/2 次即可。当然,描述出来的状态可能不合法,为使之合法,上一些限制。对一棵草,它相连的两条边不能全为 0。对有牛的位置,若此处仅有一头,则相连的边不能同为 1 或同为 2。对没有牛的位置,不准出现 12 和 21。每个非零段必须包含一头牛。据此设出状态 \(f_{i,0/1/2/3/4}\),转移比较简单,总复杂度 \(O(n)\),可解决牛和草总数较少的情形。

矩阵优化。dp 转移要求,必须从左到右依次乘上转移矩阵。将 \(L_i,R_i,A_i,B_i\) 作为关键位置,它们排序后,关心相邻关键位置之间的连续段 \([l,r]\)。依次写出 \(l,l+1,...,r\) 这些位置的转移矩阵 \(P_l,P_{l+1},...,P_{l+r}\),显然 \(P_{i+M}=P_i\)。这是循环节。我多次强调,如果知道循环周期,就可对周期取模分组。将同一组内转移矩阵依次乘起来得到组矩阵。组矩阵只有 \(O(n+m)\) 种,对于组矩阵极长相同段做快速幂即可。想要从左到右依次求出下一种组矩阵,可以用线段树维护组矩阵,只有单点修改。

T-079 P10789 25-11-20 B+

首先有一个 \(O(n^2)\) 状态的暴力 dp,没有任何前途的。有两种操作,哪一种更关键?只需关心“冲刺”操作,以之为框架,相邻“冲刺”之间用连续的“滑落”操作填充。可据此改成断点转移,状态 \(f_i\) 表示从 \(i\) 到根的方案数。则 \(f_i=\sum_{j\in \mathrm{sub}_i}\sum_{k\in\mathrm{ancestor}_i}[d_k\in [d_j-r_j,d_j-l_j]][d_k<\min_{t\in\mathrm{path}_{i,j}} (d_t-h_t)]f_k\)。枚举 \(j\),则 \(d_k\) 是一段区间,用前缀和优化可做到总复杂度 \(O(n^2)\)

改变贡献主体,以 \(k\) 为主体去贡献 \(i\)。除了上述转移式中,用中括号标出的两个限制以外,还需要保证不会出现一个 \(f_k\) 没有被计算完全,就去更新别人的情形。这一条好办,只需保证,利用 \(f_k\) 更新他人(称为松弛 \(k\))之前已经松弛过其所有祖先。依照某种类 dfs/bfs 序依次松弛即可。然后看第二个限制,这个东西只跟相对大小关系有关,可以把 \(d_i,d_i-h_i\) 联合排序转成 01 序列问题。对于一个 \(k\) 具体来说,若将 \(d_p-h_p\le d_k\) 的节点标白,其余节点标黑,则合法 \(i,j\) 必须在同一黑连通块中。换句话说,我们是依 \(d_k\) 从小到大枚举 \(k\)(是个 bfs 序,顺便满足了松弛顺序限制),每次先将新的 \(d_p-h_p\le d_k\) 的节点标白,再枚举一下 \(i\) 询问有多少 \(i\) 子树内,且和 \(i\) 处于同一黑连通块的节点 \(j\) 满足 \(d_k\in [d_j-r_j,d_j-l_j]\),设此数量为 \(q\),令 \(f_i\) 加上贡献 \(qf_k\)。还是 \(O(n^2)\) 的。

这是一组本质相同询问,改变贡献主体,以 \(j\) 为主体。关注原有第一个限制,在扫描 \(k\) 的过程中,\(j\) 合法的时刻是一段区间,可以扫描线维护。现在就需要在每次标白新元素之后,再枚举 \(j\)(这个 \(j\) 得是合法的),从它往根节点方向引一条极长黑链,给黑链上的所有节点都加上 \(f_k\)。运用“到根链加/单点查-单点加/子树查对称”,改成在所有 \(j\) 位置的标记数组 \(t_j\) 加上 \(f_k\),极长黑链末端 \(e\)\(t_e\) 减去 \(f_k\)。当枚举到节点 \(x\) 打算松弛它的时候,先查一下子树 \(t\) 和,据此求出 \(f_x\),然后再用它去更新别人。

那显然需要用线段树维护 \(t\),为了支持区间查询 \(t\),线段树节点除了维护本节点所在区间 \(t\) 和以外,还需要维护本区间“合法 \(j\) 个数” \(c\) 和(\(c_i=1\) 表示 \(i\) 合法,反之表示它不合法)。你会发现所谓“令所有 \(t_j\) 加上 \(f_k\)\(t_e\) 减去 \(f_k\)”就相当于令所有位置 \(t_j+=c_jf_k\),打标记即可。你可能会说,\(t_e\) 减去 \(f_k\) 在此没有体现,其实当初把 \(e\) 改白(连通块断裂)时,对 \(c_e\) 做合适的修改(类似于差分)即可。\(j\) 的合法性发生改变时(合计 \(O(2n)\) 次),也是只需修改 \(O(1)\)\(t,c\) 就行了。松弛节点之前求 \(f\) 时,依旧直接子树查。

T-080 CF1656G 25-11-24 B

这里有两个限制,一个是“只有一个置换环”,另一个是“\(a_{p_i}\) 是回文串”。限制很复杂,难以直接构造,可以用调整法,先满足其中一个,再调整,使之满足另一个。那先满足较容易达成的限制,假设已有一个回文的解。此时,调整的目的是减少置换环,对调整的要求是保持回文性质不变。最基本的,若能保持每个 \(a_{p_i}\) 的值在调整前后都不变,则回文性显然不变嘛。由此导出的调整是,找出一对满足 \(a_{p_i}=a_{p_j}\) 且不在同一置换环的 \(i,j\),交换 \(p_i,p_j\),则两环合并。但这种调整,不能将任意初始回文解变成最终解,可能会出现“\(a_x\) 相同的 \(x\),都在同一置换环内”的情形。那怎么去处理呢?

一种想法是,直接构造合适的初始解,使之能够调整成功。但在本题中,对“合适解”的限制不明晰、很复杂,不行。看来是调整法的能力太弱,能不能增加调整方式?具体来说,原来的方式能够在 \(a_{p_i}\) 数组不变的要求下,试遍所有可能性。但现在 \(a_{p_i}\) 随便确定是不行的,故需要增加“改变 \(a_{p_i}\) 数组”的调整。调整必须保持回文性,回文性是什么,\(a_{p_i}=a_{p_{n-i+1}}\),那只需把 \(p_i,p_{n-i+1}\) 绑定在一起。故每次任取 \(i,j\),同时交换 \(p_i,p_j\)\(p_{n-i+1},p_{n-j+1}\)。只用调整法,即可把任一初始解,调整成同时满足两个限制的最终解。

posted @ 2025-12-29 21:01  timefinder  阅读(2)  评论(0)    收藏  举报