2025.3

3.10

A. 优秀子序列

这题显然是有正经做法的,但是我懒得想。

注意到序列中显然会存在相邻两项距离不超过 \(10\),所以我们可以随机一对距离不超过 \(10\) 的位置,拿这两个数扫序列求一遍答案。

这对位置在答案子序列中的概率不会太低,重复足够多次即可保证正确率。

B. 尾端操作

原题是 UOJ191。

看到这题大概很容易想到向量集那题,但是直接二进制重构不能撤销,复杂度是错的。

稍微魔改下二进制重构,允许存在两个大小为 \(2^k\) 的组,有三个 \(2^k\) 组时再合并其中两个。

然后撤销如果撤到一个完整的组直接删掉就好了,这样均摊复杂度还是对的。实现的时候考虑线段树,同一层的下一个兄弟插满时再建出来自己的凸包即可。

复杂度还是 \(O(n\log^2 n)\) 的,但是空间卡不过去,发怒。

C. 拼点游戏

这么魔怔的。

显然我们只会在序列中选择偶数个位置,假如选了 \(a_{i_1},a_{i_2},\ldots,a_{i_m}\),点数应该就是 \(\sum_{i=1}^{\frac{m}{2}}(a_{2i}-a_{2i-1})\),也就是每两个分一组,每组后减前这样。

考虑差分数组 \(d_i=a_{i+1}-a_i\),那么 \(a_x-a_y=\sum_{i=y}^{x-1}d_i\),原来选一组体现在差分数组上就是一段区间和。所以可以转化掉题意,变成随便在差分数组上选一个子集,显然差分数组子集的选取和原来的选法一一对应,所以这是正确的。

正好我们的修改是区间加,所以差分数组是好维护的,那第一问很好办了,答案就是差分数组的正数之和。

考虑第二问,对于两个差分数组中正数构成的连续段之间的部分,能做的就是让这两个段的端点相互靠拢但是不能接上。显然为了减少点数,我们只会保留这中间的最大值,所以每两段之间的空隙操作后会使点数增加 \(sum-max\)

把每个段的 \(sum-max\) 插到平衡树里,查询直接平衡树上二分即可,写起来很史,\(O(n\log n)\)

3.11

A. 三叶虫推箱子

先考虑定住根怎么做。先观察性质,对于任意一个点,假如我们派了 \(p>1\) 个三叶虫进入它的子树,\(1<q\le p\) 个三叶虫出来,这种方式一定是不优的,我们可以改成只派 \(p-q\) 个三叶虫进入这棵子树。更进一步,如果只派了一个三叶虫遍历一棵子树再回来,那么可以简单地认为这棵子树被删去了,删去的代价是子树边权和的两倍。

然后就可以简单地写出来一个树背包,这个背包换根是容易的,预处理儿子前后缀的背包即可,可以做到 \(O(nk^2)\)

B. 旅行岁月

啥都没想出来。

随便选取若干个半平面求交之后,平面中没被覆盖的部分一定形如一个凸壳或者凸包,同时选的每个半平面都会是它的一条边,否则去掉它半平面交不变且代价更小。

尝试对横坐标扫描线,此时可以观察到的一点就是这个凸多边形与一条 \(x=i\) 的直线最多只会有两个交点,所以尝试直接记录,也就是维护 \(f_{i,j,k}\) 表示当前考虑到 \(x=i\) 凸多边形的两条边界对应的半平面编号为 \(j,k\) 时最小代价。朴素的转移可以直接枚举两个边界换成了啥,显然可以加个分布转移优化摊到 \(O(1)\) 转移,这样最后的复杂度是 \(O(n^2m)\) 的。

3.12

Malfunctioning Typewriter

一句一句地填因为顺序是乱的所以很烦人,大概是行不通的。

考虑转换下思路,对于每一句一位一位地填,那么每次填的限制形如对于某一种相同前缀的位置接下来要填 \(a\)\(0\)\(b\)\(1\) 这种,而且填的时候对顺序没有任何限制,这就是这种思路的好处。

前缀的限制立马想到 trie,直接把所有串插到 trie 里,先提前 dp 一个决策 \(f_{i,j}\) 表示填 \(i\)\(0\)\(j\)\(1\) 的最高概率,那么 trie 上一个点 \(u\) 对答案的贡献就是 \(f_{siz_{ls},siz_{rs}}\),直接把每个点的贡献相乘即可,是 \(O(nm)\) 的。

Rectangle Intersection

啥玩意哦,这么诈骗。

坐标范围不大,先把矩形拆成四条边,尝试对每个格子预处理上下左右最近的边,直接统计这四条边围城的四边形面积即可,因为是最小值所以一定能统计到,注意没被矩形覆盖的点不能统计,差分实现容易做到 \(O(nm+k)\)

CF1558E Down Below

比较好玩的题,思路确实挺有趣。

显然先考虑二分答案 \(mid\),转成判定问题。如果没有不能立刻走回头路的限制,问题就很容易了,从当前已经访问过的点向周围扩展,每次找到 \(a\) 最小的走过去即可,大概能用一个小根堆维护。

加上这一条限制,仍然考虑延续这个思路,维护一下当前扩展过的点集 \(S\),但是额外加一条限制:\(S\) 内的点必须能在不立马返回的情况下两两可达。这么限制的好处是消除掉了扩展的后效性:如果你能找到一个不在 \(S\) 内且满足条件的点,直接把它加入 \(S\) 显然没有任何影响,而找不到的时候说明就是无解。

至于如何找到扩展的点,可以尝试每次找一个满足权值限制的环,把环上的点一起扩展进去,显然无论环上的点都没扩展过还是有一部分已经在 \(S\) 内都是合法的,具体的实现从 \(S\) 内的每个点出发 dfs 即可,最多进行 \(n\) 轮扩展,每次是 \(O(n+m)\)

带上二分,这题最后的复杂度就是 \(O(nm\log V)\)

CF1342F Make It Ascending

考虑一个最终序列的生成方式,形如每次选一个子集, 把它们加在一起。对于每次选择,我们要求子集内的数的和递增,同时最后加到的下标递增,然后只需要最小化选择次数。

这个形式看着就很好做了,直接对着 dp,设 \(f_{i,j,S}\) 表示当前的和为 \(i\),最后一个下标为 \(j\),选择的集合为 \(S\) 时最小的选择次数,因为值域太大所以可以互换下值域和定义域,也就是把 \(i\) 那一维改成操作次数,记录最小的和。

直接转移就是 \(O(3^nn^2)\) 的了,但是要输出方案有点唐。

3.13

ARC114E Paper Cutting 2

这个模型太经典了,转化成随机操作排列,容易证明不影响概率。

限制只有排列中一个数必须出现在另外某些数之前,这是好算的,\(O(n\log p)\)

P8391 [BalticOI 2022] Event Hopping (Day1)

发现向右跳一步的时候有多种选择,不知道该选哪个。

如果反过来改成向左跳呢?此时发现决策唯一,一定是选择跳到右端点被自己包含的区间中 \(l\) 最小的那个,其它部分不需要考虑。

然后就可以直接倍增了,\(O(n\log n)\)

CF1580D Subsequence

怎么还他妈是若只题,这是哪门子的 *2900,感觉千位数改成 \(1\) 真的一点问题没有的。

看上去要求的东西比较复杂,尤其是后面涉及到区间最值,那不妨用笛卡尔树转化掉,变成子序列两两在笛卡尔树上 LCA 的点权和。

这个就太好算了,直接在笛卡尔树上做个树背包即可,\(O(nm)\)

CF2077E Another Folding Strip

感觉就是读了遍官方题解,确信

CF1608F MEX counting

这个题就很有价值了。

尝试直接 dp,思考下状态的设计。首先是当前考虑到第 \(i\) 个以及目前的 \(mex\)\(j\)(这里显然可以先注意到有用的 \(j\) 只有至多 \(2k+1\) 个),然后这些肯定是不够的,你加入的数如果恰好是当前的 \(mex\) 你需要其它信息来更新。但是我们显然不能直接记大于 \(mex\) 的值填入的状态,这是指数级的。

然后我就卡在这里不会做了。

考虑延迟计算贡献,填的数如果大于当前的 \(mex\) 先不要着急去确定它填的啥,用一个类似打标记的方式先记下来,等到它会影响 \(mex\) 的取值的时候再决定具体填什么。更进一步地,我们其实还只关心大于 \(mex\) 的数的种类数,可以写出来现在的状态:设 \(f_{i,j,k}\) 表示考虑了前 \(i\) 个数,当前的 \(mex=j\),大于 \(mex\) 的数有 \(k\) 种且均未确定的方案数。我们惊奇地发现这个状态转移非常简单:

  • 如果填了一个 \(<mex\) 或者某种出现过的大于 \(mex\) 的数,有 \(f_{i,j,k}\times(j+k)\to f_{i+1,j,k}\)
  • 如果填了一种新的大于 \(mex\) 的数,有 \(f_{i,j,k}\to f_{i,j,k+1}\),不带系数。
  • 否则说明填了一个等于 \(mex\) 的数,枚举新的 \(k'\),则有 \(k-k'\) 种原来大于 \(j\) 的数会是连续的从而影响 \(mex\),新的 \(mex\) 值会变为 \(j+1+k-k'\),所以有 \(f_{i,j,k}\times\frac{k!}{k'}\to f_{i+1,j+1+k-k',k'}\)

当前的状态数是 \(O(n^2k)\) 的,转移是 \(O(n)\) 的,所以复杂度是 \(O(n^3k)\),当然你也可以枚举新的 \(mex\) 转移变成 \(O(n^2k^2)\),但是都是一样过不去的。

考虑优化转移,瓶颈在第三种情况,注意到转移到的状态后两维加起来是定值 \(j+k+1\),所以可以考虑分布转移,每个 \(f_{i,j,k}\) 先乘上 \(k!\) 的系数转移到临时数组 \(tmp_{j,j+k}\),那么 \(f_{i+1,j,k}\leftarrow\sum_{j'<j}\frac{tmp_{j',j+k}}{k'!}\),前缀和优化即可。

此时转移的复杂度被摊成了 \(O(1)\),最后复杂度 \(O(n^2k)\),可以通过。

3.14

A. 定向

是 ARC092F。

很简单,首先先对原图跑一下强连通分量,然后简单分类讨论一下。对于一条连接了不同 SCC 的边,假设两个 SCC 的编号分别为 \(u,v\),如果删去这条边之后 \(u\) 还能到达 \(v\),则说明这条边反向会导致 SCC 合并。而 SCC 内部的一条边 \(u\to v\),如果删去之后 \(u\) 不再能到达 \(v\),则这个 SCC 就会被拆散然后爆掉。

也就是说,我们只需要对每条边 \(u\to v\) 判断删去这条边 \(u\) 是否还能到达 \(v\) 就能知道答案了。考虑一个经典的做法,对每个点的出边处理出保留一个前后缀的可达性即可。

使用 bitset 可以做到 \(O(\frac{n^2}{w})\) 一次遍历全图,最后是 \(O(\frac{n^3}{w}+m)\) 的。

B. 染色

是 ARC089F,挺牛的题目。

场上先思考了特殊性质,发现只需要保证操作次数最优然后对着 dp 就可以了。

实际上这个的启发性是很强的,我们不妨就去思考下先判定一个局面的最优操作方式,然后计数的时候才能判定可否生成。

小球会被白色分割成若干个红蓝连续段,如果一个连续段是纯红的只需要一次红色操作就可以解决,否则的话第一步肯定是先将这段全染红,第二步肯定会染一些蓝色,然后发现后续就会对颜色没有限制,只需要保证操作总数是 \(c+1\) 就能染出来这一段(\(c\) 是这一段内的蓝色极长段数量)。

那么对于一个确定的局面,假设有 \(x\) 个纯红段和 \(y\) 个红蓝段(包括纯蓝),先在操作序列里取出前 \(y\) 个 RB 再取 \(x\) 个 R,\(c+1\) 的限制可以贪心分配,这样就完成了判定。

然后其中一个做法就是你直接写一个划分数复杂度的爆搜去搜所有红蓝段,稍微压缩下状态复杂度就不会太高,大概是 \(\frac{n}{2}\) 的划分数。或者可以枚举 \(x,y\) 跑个多项式复杂度的 dp,最优是 \(O(n^5\log n)\) 的,在本题表现完全不如前者。

[ZJOI 2022] 树

\(F(S)\) 表示第一棵树非叶子集合为 \(S\) 的方案数,\(G(S)\) 表示第二棵树非叶子集合为 \(S\) 的方案数。

容斥,设 \(f(S)\) 表示第一棵树的非叶子集合为 \(S\) 子集的方案数,\(g(S)\) 表示第二棵树的非叶子集合为 \(S\) 子集的方案数。

根据子集反演,\(F(S)=\sum_{T\subseteq S} f(T)(-1)^{|S|-|T|}\)\(G(S)=\sum_{T\subseteq S}g(T)(-1)^{|S|-|T|}\),所以:

\[\begin{aligned} ans&=\sum_{S\cap T=\emptyset}F(S)G(T)\\ &=\sum_{S\cap T=\emptyset}(\sum_{S'\subseteq S}f(S)(-1)^{|S|-|S'|})(\sum_{T'\subseteq T}g(T)(-1)^{|T|-|T'|})\\ &=\sum_{S\cap T=\emptyset}f(S)g(T)(-2)^{n-|S|-|T|} \end{aligned} \]

然后对着 dp,设 \(f_{i,j,k}\) 表示考虑前 \(i\) 个点,\([1,i]\) 里有 \(j\) 个在 \(S\) 中,\([i+1,n]\) 里有 \(k\) 个在 \(T\) 中的带容斥系数的方案数即可,显然是 \(O(n^3)\) 的。

CF1452G Game On Tree

一开始想的是按照 \(k\) 个点切成若干个连通块,每个点朝最远的跑,也就是直接跑个多源 bfs,但是显然是错误的。

思考一下,Alice 走的路径一定是先走到一个点然后原地不东,那我们不妨转化下思路,改为枚举一个最终点去更新其它点的答案。先保留上面多源 bfs 的结果,也就是跑出 \(f_u\) 表示 \(u\) 离最近的特殊点有多远,可以观察到从 \(u\) 开始能走到 \(v\) 并停留在 \(v\) 的充要条件其实就是 \(dis(u,v)<f_v\),所以相当于是 \(v\) 会对邻域内距离小于 \(f_v\) 的点答案与 \(f_v\) 做 check max。

考虑直接点分治,处理出跨过分治中心的更新即可,是 \(O(n\log n)\) 的。

3.15

A. 秦始皇

唐氏 Ad-hoc 题。

如果全 \(0\) 和全 \(1\) 的输出相同,那么显然全 \(0\) 是一组最优解,否则考虑将全 \(0\) 从左到右逐位变成全 \(1\),中间必然存在一个时刻,输出第一次与全 \(0\) 不同,也就是 说将这个时刻与上一个时刻之间改变的那一位设为 \(x\),之前的位置设为 \(1\),之后的位置设为 \(0\),是一组解。

注意到这个好像有单调性,所以套个二分就是 \(O(n\log n)\) 的。

[NOISG 2021 Finals] Pond

最 naive 的做法是直接区间 dp,状态数就已经爆了。

考虑想一个有前途的计算贡献的方式,重新分配贡献,首先点 \(i\) 有基础贡献 \(|x_i−x_k|\),除此之外,不妨设 \(i<k\),则每一次 \(x\in[i+1,k]\to y>k\) 的移动都会造成一来一回 \(2(x_y−x_x)\) 的贡献。因此,我们设 \(f_i\) 表示到达 \(i\) 时的最小额外贡献,则我们可以写出一个和“对侧已经算到哪里”无关的转移式子:

\[\begin{aligned} f_j&=f_i+(n-i)\times 2(x_i-x_j)&(j<k,i\ge k)\\ f_j&=f_i+(i-1)\times 2(x_j-x_i)&(j>k,i\le k) \end{aligned} \]

注意到转移顺序不确定,大概需要跑个 dijkstra,但是这张图边数在 \(O(n^2)\) 级别。

转移式子可以拆成斜率优化,考虑使用李超树加速,稍微分析一下转移式子就可以知道每次已经扩展完的点构成一段区间,下一步只会向左或者向右扩展一步,直接模拟就是 \(O(n\log n)\) 的。

[JOISC 2023] Chorus

首先先调整一下,得到最优的匹配方式一定使得第 \(i\) 个男河狸在第 \(i\) 个女河狸前面,更直观的分析方式是画折线图。

再进一步地,一定是划分为若干段 \([l,r]\),使得每段内是第 \([l,r]\) 个男河狸接第 \([l,r]\) 个女河狸。

就此 dp,设 \(f_{i,j}\) 表示考虑了前 \(i\) 个男女河狸,当前已经划分了 \(j\) 段的最小交换次数,转移形如 \(f_{j,k}+w(j+1,i)\to f_{i,k+1}\) 这样,其中 \(w(l,r)\) 表示将第 \([l,r]\) 个男女河狸调在一起的最小代价,可以预处理得到。

观察到 \(w(l,r)\) 满足四边形不等式且 dp 关于第二维是凸的,可以直接先上个 wqs 二分去掉第二维,每次 dp 还可以再拆成斜率优化的形式做到单次 \(O(n)\),所以最后就 \(O(n\log V)\) 做完了。

[CCO 2023] Line Town

好牛的题目。

首先这个交换取反的操作比较奇怪,把它转化一下,将序列的偶数位置给取反,这样就变成了普通的邻项交换。

此时的限制变成了,对于奇数位置有 \(a_i+a_{i+1}\le 0\),对于偶数位置有 \(a_{i}+a_{i+1}\ge 0\)

分析一下这个限制,首先 \(0\) 肯定是填在序列的中间一段,不可能隔开,否则一定不合法。

其次可以注意到序列的绝对值是先变小后变大的,找到序列中相邻且同号的位置(或者是中间的一段 \(0\)),它的左右两边一定都是正负交替的,且绝对值变化是左减右增,而如果不存在这样的位置那么整个序列的绝对值就会直接变成单调不增或单调不降了,所以这个看似更麻烦的限制其实性质很好。

接下来该怎么做?因为这种谷形的结构,我们考虑对着两部分 dp 然后拼起来。

考虑把数按照绝对值从大往小加入,设 \(f_{i,0/1,0/1}\) 表示当前已经加入了绝对值 \(\ge i\) 的数,且左边/右边的最后一个加入的数是正数/负数的最小代价(右边加入的时候是每次插到最左侧)。如何计算转移代价?考虑进行一些分类,用树状数组计算出每种放数方案的代价即可直接转移,具体的分类方式洛谷的前两篇题解非常详细,虽然 VainSylphid 老师是不是有个 typo。

最后的复杂度是 \(O(n\log n)\)

3.17

A. 森林之王

糖丸了。。。。。

先曼哈顿转切比雪夫,转化为存在一个正方形使得三个点都在边界上。

什么样的三个点画不出这样的三角形呢?只有中间的那个点严格位于左右两个点构成的矩形内部才会倒闭。枚举这个中点以后相当于以它为原点,求出落在第一/二/三/四象限里的点数,扫描线 + BIT 即可维护,\(O(n\log n)\)

CF1548E Gregor and the Two Painters

哪里有我看了十几分钟就会做了的 *3400 啊???

连通块不是很好刻画的样子,考虑找个代表元:每个连通块用 \(a_i+b_j\) 最小的位置去代表它。

我们先对 \(a,b\) 跑一个单调栈求出左右第一个小于的位置,就记作 \(pre_i\)\(nxt_i\)。 考虑一对 \((i,j)\) 成为代表元的充要条件,首先 \(a_i+b_j\le x\) 是必然的,回到网格图看,我们显然还要求 \((pre_i/nxt_i,j)\)\((i,pre_j/nxt_j)\) 是不能与 \((i,j)\) 连通的,因为它们的值更小,这时候就要求 \((pre_i,i),(i,suf_i)\) 中有一个数能挡住 \(i\) 走到 \(pre_i\)\(nxt_i\)\(j\) 同理,询问出区间最大值之后相当于互相给对方限制了一个下界,结合上面的 \(a_i+b_j\le x\) 就变成了互相限制一个区间。

此时问题就行如,每个 \(1\le i\le n\) 有一个值 \(a_i\) 和区间 \([u_i,v_i]\),每个 \(1\le j\le m\) 有一个值 \(b_i\) 和区间 \([p_i,q_i]\),求满足 \(a_i\in[p_i,q_i]\)\(b_i\in[u_i,v_i]\)\((i,j)\) 点对数目。

直接扫描线 + BIT 即可,做完了,\(O(n\log n)\)

怎么这么像双序列拓展,是不是就是因为这个我才会做的。

[JOISC 2023] Cookies

考虑 Hall 定理一个网络流形式的推广:源点点集 \(A\) 的每个点流量为 \(a_i\),汇点点集 \(B\) 的每个点流量为 \(b_i\),则左部能流满的充要条件是对于任意的 \(S\subseteq A\),都有 \(\sum_{x\in S}a_x\le \sum_{y\in N(S)}b_y\)

考虑网络流模型,每种饼干有 \(a_i\) 的流,每个盒子有 \(cnt_j\) 的流,每种饼干向每个盒子连 \(1\) 的边。考察一个盒子组的合法条件:

  • \(\sum a_i=\sum cnt_j\)

  • 盒子一侧流满。由 Hall 定理知这等价于要求对于任意一个盒子子集 \(S\),有 \(\sum_{j\in S}cnt_j\le\sum_{i=1}^n\min(|S|,a_i)\)

第二个限制显然落在最大的若干个 \(cnt\) 上。于是考虑从大往小选盒子,设 \(f_{i,j,k}\) 表示前 \(i\) 大的,选了 \(j\) 个 cnt,此时和为 \(k\) 是否可行。注意到有限制 \(j\le\frac{sum}{siz_i}\),于是前两维总共是 \(O(n\log⁡n)\)。整个 dp 的转移显然可以用 bitset 加速,然后这个 dp 是 \(O(\frac{nm\log n}{w})\) 的。

然后我们得到了一组合法的 \(cnt\),构造方案是容易的,直接每次贪心取最大的若干 \(a_i\) 即可。

3.18

A. 写作与沟通

抽象的。

首先对于二度点,角度对答案直接有贡献,先累加上。

对于四度点,我们可以先任意选个代价最小的匹配方案,这样唯一的问题是图可能变成若干个不交的欧拉回路。

显然如果一个方案裂出了两个欧拉回路,换个方案就能再合并,以两个方案的代价差为边权,对当前的欧拉回路跑 MST 即可。

B. 投放广告

是 CF1034D,离场切 *3500 最近的一集。

类似非常多道题,考虑先二分第 \(k\) 大值 \(mid\),则我们需要求出覆盖长度 \(\ge mid\)\([l,r]\) 有几对,最后再求和。

首先给所有 \(b_i\)\(1\) 让区间长度变成 \(r-l+1\)

尝试对一维扫描线,这里选择扫区间序列的右端点 \(r\),则需要维护出所有 \(f_{l}\) 表示 \([l,r]\) 内的所有区间并的长度。加入区间 \([a_r,b_r]\) 时,数轴上这些位置在以后的任意时刻只要左端点 \(\le r\) 就能对长度产生贡献。换句话说就是对数轴上的点维护 \(lst_i\) 表示编号最大的覆盖这个点的区间,那么 \(f_l=\sum_i[lst_i\le l]\),加入区间 \([a_r,b_r]\) 相当于区间将 \(lst_i\) 推平为 \(r\),容易使用 ODT + 线段树在更新 \(lst_i\) 的同时维护出 \(f_l\) 的变化。

注意到固定 \(r\) 之后 \(f_l\) 是单调不增的,所以可以直接线段树二分找到最大的 \(l\) 使得 \(f_l\ge mid\),这样就得到了每个 \(r\) 合法的 \(l\) 所对应的前缀,直接再前缀求和也就得到了和。

直接做是 \(O(n\log n\log V)\) 的,无法通过。注意这里每轮 check ODT 得到的区间操作是固定的,所以 ODT 要放在二分外,否则就会像我赛时一样连 70 分都拿不到,我得到了高贵的 35 分。

考虑优化,同样因为 \(f_l\) 这个序列单调不增,利用这点我们可以干掉线段树:区间加换成差分维护,双指针找到最大的合法的 \(l\)。这样单次 check 就是 \(O(n)\) 的,最后总共是 \(O(n\log V)\) 的,可以通过。

C. 星球旅行

好久没补 T3 了呢,是 CF718E 把字符集改成了 \(18\),感觉这种题完全没法独立分析出来。

首先有一个观察是,任意路径一个字符不会出现超过两次,所以答案的上界是 \(36\)

\(dis(i,j)\) 表示 \(i,j\) 之间的最短路,\(f_{i,c}\) 表示 \(i\) 走到某个字符为 \(c\) 的位置的最短路。首先如果不使用传送门,那么距离就是 \(|i-j|\),否则枚举用过的某种传送门字符 \(c\),使用 \(f_{i,c}+f_{j,c}+1\) 去更新。因此,\(dis(i,j)=\min(|i-j|,\min_c f_{i,c}+f_{j,c}+1)\).

更进一步地,设 \(g_{x,y}\) 表示某个字符为 \(x\) 的位置到某个字符为 \(y\) 的位置的最短路,那么 \(f_{i,c}\le g_{a_i,c}+1\)

二分答案 \(mid\),求出 \(dis(i,j)\ge mid\) 的点对数。考虑直接求 \(\min_c f_{i,c}+f_{j,c}+1\ge mid\) 的点对数,因为 \(|i-j|<mid\) 的情况可以扫一遍特判掉。

枚举 \(i,c\)\(a_j=w\),那么 \(f_{i,c}+f_{j,c}+1\in\{f_{i,c}+g_{w,c}+1,f_{i,c}+g_{w,c}+2\}\)

分析一下,如果 \(f_{i,c}+g_{w,c}+2<mid\) 那么所有 \(a_j=w\)\(dis(i,j)\) 都会小于 \(mid\),不合法。

否则若 \(f_{i,c}+g_{w,c}+1<mid\),那么必须有 \(f_{j,c}>g_{w,c}\),否则最短路就会变成 \(f_{i,c}+g_{w,c}+1\)

对于第二种情况,先对每个 \(j\) 预处理出 \(st_j\) 表示使得 \(f_{j,c}=g_{a_j,c}\)\(c\) 构成的集合。改为先枚举 \(i\)\(w\),求出使得 \(f_{i,c}+g_{w,c}+2<mid\)\(c\) 的集合 \(S\),则变成求多少个 \(a_j=w\)\(st_j\)\(S\) 无交,这个也可以提前高维前缀和预处理。

时间复杂度 \(O(nk^2+k^22^k\log k)\),其中 \(k=|\Sigma|\)。更进一步地,找到 \(T=\max_{i,c}f_{i,c}\) ,其实还可以发现 \(ans=\{T,T+1\}\),所以二分可以再省去,复杂度变为 \(O(nk^2+k^22^k)\)

3.19

CF603E Pastoral Oddities

首先思考下合法的充要条件。显然的事实是,一个连通块我们只需要去考虑它的任意一棵生成树,因为非树边等价于路径上的边全选。

显然叶子的父亲边必须要选,随便归纳一下就可以得到,合法当且仅当图中不存在奇数大小的连通块。

因为加边不会增加奇数连通块,所以策略肯定是边从小到达排序后加到没有奇数连通块立马停止。所以发现了有个单调性在:询问的答案肯定是开始可能会不合法,然后越来越小。

所以直接上整体二分!设 \(solve(l,r,ql,qr)\) 表示 \([ql,qr]\) 中的询问答案在 \([l,r]\) 内,同时这里要求编号 \(<ql\) 且权值 \(<l\) 的边已经加入了并查集,然后直接递归做就好了,\(O(n\log^2 n)\)

CF1764H Doremy's Paint 2

我觉得挺难的啊其实。。。

先断环为链,尝试去刻画一个颜色被保留的条件。

首先这个颜色必须是某个 \([l_i,r_i]\) 的左端点,考虑进行了这次操作之后的事情,只要 \([l_i,r_i]\) 内的颜色还有一个存活的,\(a_{l_i}\) 的颜色就还在,也就是我们只需要知道只考虑后续操作时 \([l_i,r_i]\) 内颜色存活时间的 \(\max\)

所以这启发我们倒着扫描线,考虑维护这么一个东西,设 \(t_{i}\) 表示 \(a_i\) 这个颜色在哪次操作后消失,加入 \([l_i,r_i]\) 之后:

  • \(t_{l_i}\leftarrow\max_{j=_{l_i+1}}^{r_i} t_j\)
  • \(\forall j\in[l_{i}+1,r_i],t_j\leftarrow i\)

查询时也就是求 \(\sum_{i=1}^n[t_i\ge i+k]\),显然可以使用 ODT+BIT 维护这些操作,时间复杂度 \(O(n\log n)\)

CF1615F LEGOndary Grandmaster

见得多了,这次一眼就秒杀了!

考虑将偶数位取反,一次操作就变成了平凡的交换相邻位置,则 \(s\) 能变成 \(t\) 当且仅当 \(01\) 的数量一致。

至于代价如何计算,考虑统计每个缝隙被经过了几次,显然是前缀和数组对位差的绝对值求和。

直接对着 dp 就好了,时间复杂度 \(O(n^2)\)

[NOISG 2022 Qualification] Dragonfly

可以很容易地观察到一个点对子树内的询问有贡献的时刻是一个区间。

这个区间具体如何求,我们首先需要求出 \(t_i\) 表示 \(i\) 上的昆虫刚好被吃完的时间。

由于随着时间一个点上的昆虫是越来越少的,所以具有单调性,可以直接整体二分求出所有的 \(t_i\)

然后使用 dfs 序转化后就变成了简单的单点加矩形查,直接扫描线 + BIT 解决即可。

如果使用树状数组来整体二分复杂度是 \(O(n\log^2 n)\),但是其实整体二分一轮可以做到线性,也就是最优是 \(O(n\log n)\) 的。

P10408 「SMOI-R1」Apple

省选计划的模拟赛里有一题的最后一步和这个一模一样。

考虑按照值域折半,设 \(dp_{x,y}\) 表示前十位为 \(x\) 后十位为 \(y\) 的子集的数的权值和。

更新 \(a_x\) 时,只需要枚举 \(x\) 后十位的超集;询问 \(x\) 时,只需要枚举 \(x\) 前十位的子集。

时间复杂度 \(O(q2^{n/2})\)

3.21

A. 博弈论

简单题,半小时过了。

考虑 \(p=0\) 咋做,一个直观的想法就是按照编号从小到大激活所有点,然后检查已经激活的点是否在一条链上,第一次不在一条脸上的时刻就是对应的答案,而这个是好维护的,只需要一些简单的分讨。

更进一步地可以发现这个信息是可以直接合并的,所以倍长一下序列之后可以开个倍增数组,预处理出来每个位置向后跳若干步的点是否在一条链上,是的话端点是什么,直接从每个位置开始跳即可,复杂度 \(O(n\log n)\)

B. 云朵

CF833E,*3400/tuu/tuu。

显然,我们只需要关心所有 \([0,x]\) 在最优策略下能得到多少空格,其中 \(x\) 是某个区间的左右端点,这样查询显然可以二分,那就先把所有的左右端点扔进去离散化下,离散化后的序列记作 \(v_i\)

考虑动态维护以下信息,注意全都只考虑 \([0,v_i]\)

  • \(f_x\) 表示只选择第 \(x\) 个区间时可以产生的空格长度。
  • \(g_x\) 表示选择了第 \(x\) 个区间和其他某个区间时可以产生的最大空格长度。
  • \(h_{x,y}\) 表示只被第 \(x,y\) 个区间覆盖的空格长度。

注意 \(h\) 这个数组的总状态数是线性的,可以用 map 存一下。

考虑 \([v_{i-1},v_i]\) 被几个区间覆盖,设 \(len=v_i-v_{i-1}\)

  • 没有被任何区间覆盖,那么可以直接将 \(len\) 加入到答案里。
  • 被一个区间覆盖时,设它为 \(x\),那么将 \(f_x\)\(g_x\) 都加上 \(len\)
  • 被两个区间覆盖时,设它们为 \(x,y\),首先将 \(h_{x,y}\) 加上 \(len\),再用 \(f_x+f_y+h_{x,y}\) 去更新 \(g_x,g_y\)
  • 大于两个区间时,可以直接跳过不管,没有贡献。

除此之外,在第一次加入某个区间 \(x\) 时我们还要初始化下它的 \(g_x\),相当于找最大的 \(f_y\)\(c_x+c_y\le C\)

每次使用 \(g\) 数组去更新当前答案即可,容易做到 \(O(n\log n)\)

C. n 进制加法

是 LOJ3225,纯口胡,并没有写代码。

先点分治,直接求出每个点到当前分治中心路径上的值构成的序列,存到主席树里,这样会得到 \(O(n\log n)\) 棵主席树。

容易发现比较其实就是字典序,所以主席树维护哈希,二分找到 LCP 即可实现 \(O(\log n)\) 比较两棵主席树。

那么我们就可以做到把每组分治中心对应的主席树提前排好序了,然后考虑一个随机二分的技巧,每次随机一个可能的答案判断是否可行,具体 check 的时候直接在每组主席树内双指针,这样期望也只会进行 \(O(\log V)\) 轮。

最后的复杂度大概是 \(O(n\log^2n\log V)\)

[SDOI 2019] 世界地图

一个显然的想法就是,维护出每个前后缀的 MST,查询时合并两棵 MST。

唯一的问题是,合并两棵 MST 复杂度还是 \(O(nm\log n)\) 的,所以尝试缩减一下 MST 的规模,使得合并的复杂度可以接受。

显然原来 MST 上大多数的边是会被保留下来的,我们可以试试只存可能被替代掉的边。考虑维护动态 MST 的过程,加入一条新边之后,我们会拉出来原来的路径最大值比较,再判断是否删去它。把一段前缀的最左右两列视为关键点的话,可以发现只有任选两个关键点之间路径上边权最大的边可以删去,换句话说这不是虚树吗!

所以维护 MST 上关键点的虚树,边权是原树上之间的路径最大值,这样每棵 MST 的点边数量就都在 \(O(n)\) 大小了。


剩下的题是在省集前的加练,其实还有好多题没放过来哎哟,懒得补了,先这样吧。

AT_iroha2019_day4_l ...好きです

因为查询的是 \(\max\),所以可以放心地通过分讨去掉答案式子的绝对值,然后发现相当于查询 \((x,0)\) 到一个点集内的点连线的斜率最值。

经典的结论是,答案只会在凸壳上的点取到,所以我们只需要知道当前时刻的凸壳即可查询答案。

有加点和删点操作,容易想到线段树分治,这里线段树上的点是出现时间包含当前区间的点,把凸壳预处理出来后线段树所有访问过的凸壳都要查询一次。

需要注意的是,先给所有点按照横坐标排序再插入,这样求凸壳的时候可以省去排序,同时查询的时候也有单调性,不然会多 \(\log\),最后是 \(O(q\log q)\) 的。

代码

YDRG005D 使一颗心免于哀伤

先考虑这么一个问题:区间插入删除直线,区间查询某
个点 \(x\) 处的点值 \(\max\) 该怎么做。

可以注意到,如果查询的区间内重复出现了多次某条直线是没有用的。换句话说,假如在区间 \([l,r]\) 插入,它对询问区间 \([ql,qr]\) 只要交集不为空就能产生贡献。

这个限制可以这样解决:开两棵线段树,修改的时候第一棵线段树在 \(\log\) 个完全包含的区间插入,第二棵线段树在所有经过的区间插入;查询的时候,第一棵线段树在所有经过的区间更新答案,第二棵线段树在 \(\log\) 个完全包含的区间更新答案。分讨所有修改和查询区间之间的关系,可以证明这么做的正确性。

插入的时候具体怎么做也是在线段树上维护凸壳,和上一题差不多。

现在还给查询的集合加入了时间的限制,其实是一个道理,只要出现的时间再有交即可,可以再用上面的方式分两种情况处理,也就是直接跑四遍线段树分治。

时间复杂度是 \(O(n\log^2 n)\) 的,四倍常数,代码没写。

UOJ462 新年的小黄鸭

直接设 \(dp_u\) 表示只考虑 \(u\) 子树内时的最小代价。

考虑一个暴力,枚举 \(u\) 子树内的一个叶子 \(v\),尝试将 \(u\)\(v\) 的整个路径划为一条链,首先挂出去的子树都先有一个 \(dp+siz\) 的贡献,然后再加上 \(u\)\(v\) 的链长取 \(\log\) 即可。

优化是简单的,直接把叶子按照 dfs 序排序后建线段树,直接维护每个叶子转移过来的代价是容易的,直接做就是 \(O(n\log^2 n)\) 的,代码

下面是云浅的做法。

我们设 \(dp_{u,x}\) 表示考虑 \(u\) 的子树且 \(u\) 上方挂了 \(x\) 条重边的最小代价,同时延迟计算重链长度的贡献。枚举儿子 \(v\) 是重儿子,那么 \(dp_{v,x+1}\) 会转移到 \(dp_{v,x}\),其它儿子 \(v'\) 的贡献就是 \(dp_{v',0}\) 加上 \(\log x\)。注意到加 \(\log x\) 可以先拆成 \(\log\) 次区间加,剩下的就相当于是对位 chkmin(可能要把下标平移下,因为是 \(dp_{v,x+1}\) 转移到 \(dp_{u,x}\),比如第二维换成 \(dep_u-x\))。

直接线段树优化显然时间上可以做到 \(O(n\log^2 n)\),但是空间复杂度比较难说,而且这个线段树合并还要下放标记,常数巨大。

有这么一个技巧,先 dfs 重儿子,儿子的线段树合并过来后立马销毁,这样祖先上至多有 \(\log\) 棵线段树,即使每棵线段树是满的空间复杂度也才 \(O(n\log n)\),这个分析还比较松,所以很没问题!但是我没写代码。

云浅说正常情况线段树合并这么做空间是线性的。

QOJ3998 The Profiteer

前面忘了,正睿搬过。

P4234 最小差值生成树

不会 LCT 该怎么办呢?自己编了个和上题一样的分治做法。

\(f_l\) 表示最小的 \(r\ge l\) 使得保留图中边权在 \([l,r]\) 内的边图连通,显然答案是最小的 \(f_l-l\)

注意到 \(f_l\) 具有单调性,考虑决策单调性分治。记 \(\text{Solve}(l,r,s,t)\) 表示已经确定了 \(f_l,f_{l+1},\ldots,f_r\) 的取值在 \([s,t]\) 内。

\(mid=\lfloor\frac{l+r}{2}\rfloor\),考虑从 \(mid\) 开始从小到大加入各种边权的边,找到第一条加入后连通的边,则 \(f_{mid}\) 的值就是它的边权 \(w\)。根据单调性,可以直接递归到子问题 \(\text{Solve}(l,mid-1,s,w)\)\(\text{Solve}(mid+1,r,w,t)\)

注意这种分治为了保证复杂度,我们需要确保每一层递归枚举到的边数总和是线性的,解决方法是直接要求边权大小在 \((r,s)\) 内的边递归前已经加入并查集内即可。

复杂度是两个 \(\log\),抢到了最优解,代码

P5314 [Ynoi2011] ODT

路径修改邻域查询,考虑对每个点的轻儿子开一个数据结构,因为树剖后一次路径加只会影响 \(\log\) 条轻边,所以也只有 \(\log\) 个点的数据结构需要修改。

对于本题就是每个点开一棵平衡树插入所有轻儿子的点权,路径加只需要改 \(\log\) 个平衡树。查询的时候,只需要把重儿子,自己和父亲的点权插入到平衡树里再查询即可,复杂度是 \(O(n\log ^2 n)\)。当然这里显然修改数比查询数多,可以平衡一下除以个 \(\log\log n\),但是没有必要。

总之是个很有启发性的题,或许邻域查询的题目都可以这么考虑。

代码,偷懒用了 pbds 竟然能过。

QOJ5020 举办乘凉州喵,举办乘凉州谢谢喵

现在我感觉我对树剖的理解深刻了一些!/fendou

先考虑询问的路径 \(x,y\) 是一条祖先后代链的情况,这里认为 \(y\)\(x\) 的祖先。首先 \(y\) 子树外的点可以直接点分治处理掉,所以只需要考虑 \(y\) 子树内的点如何统计。

直接先上树剖,这样路径上只会有 \(\log\) 条轻边。延续上一题的思想,可以考虑求出每个点所有轻儿子子树对应的信息,这样可以直接简单地累加,只有 \(\log\) 个轻儿子在路径上的点需要特殊处理。具体地,设 \(f_{u,d}\) 表示 \(u\) 子树内和 \(u\) 的距离 \(\le d\) 的点的个数,\(g_{u,d}\) 表示 \(u\) 所有轻儿子的子树内和 \(u\) 的距离 \(\le d\) 的点的个数,路径上的轻边是 \((u_1,v_1),(u_2,v_2),\ldots,(u_k,v_k)\)\(u_i\)\(v_i\) 的父亲),询问的答案就是路径上的点数,加上每个点的 \(g_{u,d}\),减去所有的 \(f_{v_i,d-1}\),加上所有的 \(f_{son_{u_i},d-1}\)\(son_u\) 表示 \(u\) 的重儿子)。

将询问差分一下,问题形如 \(O(n\log n)\) 次查询某个 \(f_{u,d}\)\(O(n)\) 次查询一个点到根的路径上的 \(g_{u,d}\) 之和。对于 \(f\) 的查询,这个比较简单,直接 dsu on tree+BIT 统计即可。对于 \(g\) 的查询,我们首先要明白一点就是树上所有轻子树的大小之和只有 \(O(n\log n)\),这允许我们直接暴力。维护一个数据结构 \(G\)\(G_i\) 存储当前点到根路径上所有 \(g_{u,i}\) 之和,遍历到一个点 \(u\) 直接枚举轻儿子子树内的点插入即可,可以树状数组维护。

最后解决一个问题:查询的路径不是祖先后代链咋办。设 \(z=\text{LCA}(x,y)\),我们可以直接将查询平凡地拆成 \(x\)\(z\) 的答案与 \(y\)\(z\) 的答案拼起来,发现多算的部分只有 \(f_{z,d}\),所以完全一致。

这样做是 \(O(n\log^2 n)\) 的,似乎简单优化后可以做到 \(O(n\log n)\),但是我暂时懒得想,而且两个 \(\log\) 跑得飞快啊!代码

P5311 [Ynoi2011] 成都七中

考虑固定住根 \(x\) 怎么做。容易发现,对于一个点 \(u\) 来说,设 \(u\)\(x\) 路径上的编号最小值和最大值分别为 \(x,y\),那么当 \([x,y]\in[l,r]\) 的时候 \(u\) 会和 \(x\) 连通。现在要做的就是在和 \(x\) 连通的 \(u\) 中再数颜色,这是容易的,直接扫描线即可解决。

这种和根的连通性相关的题继续优化一般是考虑点分治。设当前的分治中心为 \(rt\),可以发现的一个性质就是,假如点 \(u\) 上有一个询问 \((l,r,x)\)\(u\)\(rt\) 上的最小值和最大值都在 \([l,r]\) 内,那么这次询问来说 \(u\)\(rt\) 其实是连通的,所以这个询问等价于在 \(rt\) 上进行 \((l,r,x)\) 的询问。直接遍历当前的连通块,把和 \(rt\) 连通的询问做掉,不和 \(rt\) 连通的询问显然可以递归到子问题解决。

时间复杂度 \(O(n\log^2 n)\),应该也可以平衡下除以个 \(\log\log n\)代码

QOJ8547 Whose Land?

首先 \(k\) 邻域可以拆成 bfs 序的 \(O(k)\) 个区间,具体怎么拆大概可以在每种深度二分出来。

然后直接扫描线扫 \(r\),每次把一个点的 \(O(k)\) 个 bfs 序区间加入。对每个位置 \(x\) 维护 \(mx_x\) 表示最大的 \(i\) 使得 \(x\)\(i\)\(k\) 邻域内,每次加入区间显然就是对 \(mx\) 做若干次区间覆盖,而查询的答案就是 \(\sum_{i=1}^n[mx_i\ge l]\),显然可以 ODT+BIT 简单维护,复杂度 \(O(nk\log n)\)代码

P7952 [✗✓OI R1] 天动万象

非常有意思的题。

先思考一条链的情况该怎么做,每次修改操作会选择一个后缀,然后将点权整体向前平移,再删去最后一个点,这个过程可以简单地用平衡树维护。

你发现每次操作后会删点,说明这背后可能就有个均摊在。放到树上,可以发现一次操作会删掉子树内的所有叶子,那如果我们一次操作的复杂度和删去的叶子数相关就行了。

考虑给树瞎几把做个链剖分,那么当前树链的个数就等于叶子的个数。考虑给每条链开一棵平衡树,一次修改直接把子树内的每条链挨个平移(还有链顶给父亲做单点加)。考虑如何查询,首先 \(u\) 自己的链会查一个后缀 \(\max\),考虑直接把每条链的 \(\max\) 挂在链顶上,子树内其它链的贡献只需要查个子树 \(\max\),可以线段树维护。

可能有比较多的细节,有时候会删出一条链的链底不是叶子的情况,这时候它肯定恰好有一个儿子,需要把它和儿子的链合并一下,不然复杂度会错。

代码就懒得写了。

P7124 [Ynoi2008] stcm

有点深刻。

众所周知子树在 dfs 序上是区间,但是这些区间之间其实还有个更强的性质:要么不交要么包含。考虑直接分治,设 \(\text{Solve}(l,r)\) 表示当前已经加入了 dfs 序上 \([l,r]\) 以外的点,要处理跨过 \(mid\) 的子树。由于前面的性质,跨过 \(mid\) 的区间两个端点都是单调的,所以一层的询问可以直接线性次操作做掉。

显然次数是 \(O(n\log n)\) 的,代码

QOJ8006 Dinosaur Bones Digging

一个容易想到的想法是对值域从大到小扫描线,找到当前的最优区间。加入一个数会给包含它的区间 \(+1\),放在平面上相当于是散点集矩形加查全局 \(\max\),这个东西直接做可能只能 KDT 到根号,不太懂,反正不能 \(\text{polylog}\)

那背后肯定是有更强的性质在的,考虑一个区间如果被另一个区间包含,那么这个区间肯定是不优的,所以这种区间可以直接删掉。现在只剩下一些不会有包含关系的区间,经典性质就是它们按照左端点排序后右端点也有序,那么一次加点就会变成区间加,然后就能做了。

思想就是通过调整出一个更简单的结构来方便维护,感觉启发性还是很强的。

复杂度显然是 \(O(n\log n)\) 的,代码

QOJ964 Excluded Min

首先考虑一个确定集合 \(S\)\(F(S)\) 如何计算,答案是最大的 \(x\) 使得 \(\sum_{y\in S}[y<x]\ge x\)

然后思路很自然又会变成从大到小加数,再次变成矩形散点加查全局 \(\max\)。但是这里我们需要对每个区间都回答,不能把一个区间简单地删去,该怎么办呢?

包含关系的区间仍然是有性质的,具体来说如果 \(A\subseteq B\),那么显然 \(F(A)\le F(B)\)。尝试利用这一点,初始时我们仍然只保留不被包含的区间,扫描线的过程中如果有区间已经找到了答案就把它删去,然后此时会产生一些新的不被包含的区间,把它们加入即可。

具体实现比较繁琐,需要用到若干数据结构维护上面的过程,复杂度还是 \(O(n\log n)\) 的,代码待补。

P9337 [Ynoi2001] 冷たい部屋、一人

考虑 \(y_i=i\) 的时候就是相等 pair 个数,所以有矩阵乘法规约,直接想根号做法就可以了。

先对出现次数根号分治。对于次数 \(\le B\) 的数字,考虑直接枚举一对,求出区间 \(\min\)\(\max\) 之后变成 \(O(nB)\) 次修改和 \(O(n)\) 次查询的二维数点,上个分块平衡一下是单根号的。

对于次数大于 \(B\) 的数字,每种数字分开做。考虑两个相邻的出现位置,把它们中间的数缩掉,只保留 \(\min\)\(\max\),得到一个长度为 \(O(cnt)\) 的序列。把查询端点离散化掉,然后对值域跑个回滚莫队,相当于会在序列中激活一个位置然后要维护被激活的连续段,直接做就可以了。但是需要实现得相当精细,不能让离散化带 \(\log\)

反正总共就是一个根号,代码懒得写了。

CF2043G Problem with Queries

显然问题与区间相等 pair 数等难,直接想根号,还带强制在线当然是直接考虑分块了,虽然好像莫队其实有方法在线的来着。

首先散块对散块,散块对整块的贡献都是好维护的,只需要考虑整块对整块之间的贡献如何算。设 \(cnt_{i,j}\) 表示前 \(i\) 个块数字 \(j\) 出现了几次,假如询问时需要考虑第 \(l\) 块到第 \(r\) 块,我们不妨直接计算 \(\sum_{x}(cnt_{r,x}-cnt_{l-1,x})^2\),这个的结果减去区间长度再除以 \(2\) 就是真正的答案。

将平方拆开变成 \(\sum_x(cnt_{r,x}^2-2cnt_{r,x}cnt_{l-1,x}+cnt_{l-1,x}^2)\),第一项和最后一项两个平方和显然是好维护的,考虑中间那一项。直接定义 \(w(l,r)=\sum_x cnt_{l,x}cnt_{r,x}\),考虑一次修改对 \(w\) 产生的影响,把 \(w\) 看成一个矩形的话,修改相当于总共做 \(O(\frac{n^2}{B})\) 次横线加或竖线加,而我们需要查 \(O(n)\) 次单点和。

因为修改操作很多所以通过差分把修改变成单点修,对矩形 \(w\) 的每行每列开一个差分数组维护修改的增量。注意到 \(w\) 的大小是 \(\frac{n}{B}\times\frac{n}{B}\) 的,所以查询一个位置的时候可以暴力遍历它所在行列的差分数组,取 \(B=\sqrt n\) 复杂度就是正确的。

综上本题做到了 \(O(n\sqrt n)\)

P9040 [PA 2021] Desant 2

序列给定时,设 \(f_{i}\) 表示前 \(i\) 个位置的答案,显然 \(f_{i}=\max({f_{i-1}},f_{i-k}+\sum_{j=k+1}^ia_j)\),第二种转移可以前缀和,然后发现这个 dp 好像只能优化到一次线性。

能转移到 \(i\) 的位置非常简单,所以考虑把 dp 转移的 DAG 画出来,相当于是每个点 \(i\) 会向 \(i+1\) 连一条零权边,向 \(i+k\) 连一条边权为 \(\sum_{j=i+1}^{i+k}a_j\) 的边,然后每次询问相当于查询两点直接的最长路。继续发掘图的性质,把图画的方正一点,让点 \(i\) 变成点 \((\lfloor\frac{i}{k}\rfloor,i\bmod k)\),会发现这张图几乎就是一个网格图,唯一的问题是最后一列会向第一列连一些额外的边。

假如图就是网格图,可以直接分治,每次选边长长的一侧折半,枚举经过中线上的一点处理所有询问,这里因为是 DAG 每层跑一次单源最短路是 \(O(n)\) 的,且中线长度显然不超过 \(O(\sqrt n)\),所以复杂度是 \(T(n)=O(n\sqrt n)+2T(n/2)=O(n\sqrt n)\) 的。

对于本题最后一列向第一列额外多的边,直接在第一次分治的时候把额外边的端点也枚举上再跑最短路即可,这种情况只会发生在某一次分治所以不影响复杂度,仍然为 \(O(n\sqrt n)\)代码

P5064 [Ynoi Easy Round 2014] 等这场战争结束之后

遇到这种回退版本不会直接处理的时候记得考虑建操作树。

直接在 dfs 版本树的时候维护答案,我们需要一个不依赖于均摊的可撤销数据结构。首先只查询排名可以把值离散化成排列,然后考虑对值域分块,维护 \(f_{i,j}\) 表示第 \(i\) 个连通块有多少个在第 \(j\) 个块内的数字,合并和撤销的复杂度都是块的个数。

查询的时候只需要找到在哪个块内,然后暴力扫散块即可,需要一些手段卡下空间。

posted @ 2025-03-20 11:20  KingPowers  阅读(95)  评论(0)    收藏  举报