HE 集训讲题纪要

Day1: 贪心 & Dp

太困难,可能有的真的不会。

如果有写的不对的地方,请斧正。

[CERC2013] History course

弱化版:CF309E Sheep

[ICPC 2017 WF] Scenery

一个显然的贪心是选能拍照且 ddl 最近的拍照。

这个不对因为有些时间段不能拍照否则有些照片根本没法拍,比如拍照时常为 \(10\),有一个区间 \([20,35]\),那么显然 \((35-10*2,20)\) 这个区间不能拍照。

称这个区间为禁区,我们找到所有禁区,进行“显然的贪心",应该就对了。

每个拍照区间产生的禁区为 \((R-t*2,l)\)

注意到我们还要关注到不只有一个拍照目标的区间,对于任意一个区间 \([L,R]\),设里面有 \(k\) 个拍照区间,我们应该如何设置禁区呢?

注意这个区间里面可能还有已经设置的禁区,需要避开。直接从后往前贪心的放置就好了,如果这个区间放不满 \(k\) 个,直接无解。

否则可以得到最靠左的区间起始点 \(p\),将 \([p-t,L]\) 设置为禁区就好了。

注意到为了消除后效性,我们按 \(L\) 从大到小处理,每次加入向所有 \(R\) 中加入以 \(L\) 开头的要求。

AT_joisc2017_c 手持ち花火 (Sparklers)

以前模拟赛的题,现在还不会。

首先要明确一点,某一个时刻最多只有一个人手持火把,其余人要么在靠近,要么和在追他(或者跟随)。

这样对于相向而行的人,距离一定越来越近,同向而行的人,距离不变,同时火把延续的时间更长,更有机会让人得到。

因此我们可以把这个过程理解为,如果有人和拿火把的人重合,这个人献祭自己,给拿火把的人续命 \(T\) 秒,如果拿火把的人能在死之前和所有人相遇就 ok。

因此一个区间,如果两端相遇时长比所有人生命时长总和大,那肯定完蛋。否则如果中间的人不拉跨,两侧的人一定能得到火把。

将这个条件形式化的表达后,发现 \([l,r]\) 合法当且仅当 \(f_l<f_r\),且 \([l,r-1],[l+1,r],[l+1,r-1]\) 中的至少一个合法。

发现这个就是双序列扩展,直接套用即可。

CF573E Bear and Bowling

容易列出转移方程:

\[f_{i,j}= \max\{f_{i-1,j},f_{i-1,j}+j \times a_i\} \]

不是 slope trick。考虑这种取 \(\max\) 操作的另一种维护方式是对 dp 数组找分界点,前面满足第一项大,后面满足第二项大。

如果真的存在的话,那么我们直接考虑 \(f_i\) 的差分数组 \(g_j\),我们的操作相当于找到第一个加上当前项更好的地方 \(k\),插入 \(k \times a_i\),前面的部分直接继承,后面的部分全部 \(+a_i\)

考察这个位置的形式化描述:相当于找到第一个 \(f_j \le j \times a_i\),那我们只需要证明 \(f_j/j\) 有序即可,证明忽略,可以打表。

平衡树维护即可。

CF559E Gerald and Path

考虑最终形成的一定是若干连续段,问题转化为如何判定一个连续段是否合法。

可以从左往右扫描,设 \(f_i\) 表示前 \(i\) 个点可以覆盖到的最长前缀。

使用 lanterns 技巧即可 \(n \log n\) 解决。

总复杂度 \(n^2 \log n\)

有人会用这个方法做到 \(n^2\) 的请告诉我。

CF1067D Computer Game

牛逼题,我是傻逼。

第一步就不会,真是傻逼到极点了。

卡在收益的贡献可以算期望,但是升级怎么搞期望啊。

其实是,一旦有升级机会,一定会选择 \(M=b_i \times p_i\) 最大的升级,然后之后一直草这个就行了。

因此直接 \(dp\)\(f_t\) 表示还剩 \(t\) 步的时候仍然没有解锁的最大收益。

\[f_{t+1}=\max\limits_{i=1}^{n}(p_{i} \times (t\times M+a_i)+(1-p_i) \times f_{t}) \]

看到参变相乘,相当斜率优化。

\[f_{t+1}=\max(p_i \times (M \times t-f_t)+p_i \times a_i)+f_t \]

先维护直线 \(y=p_i \times x+p_i \times a_i\) 的上半平面交。

对偶后是 \((p_i,p_i \times a_i)\) 的下凸壳。

注意这个 \(M \times t-f_t\) 单调递增,所以有单调性。

注意到转移点之后 \(O(n)\) 个,定下转移点后可以矩阵快速幂,然后判断转移是否出界即可。注意如果转移不优的话得到的 \(f_t\) 更小更容易出界,因此不必担心。

CF933E A Preponderant Reunion

我们可以钦定最终哪些位置是 \(0\),注意到没必要钦定 \(0\) 的连续段长度超过 \(2\) 的部分。

对于连续段长度为 \(1\) 的,下界显然为 \(a_i\),对于连续段长度为 \(2\) 的,下界为 \(\max(a_{i-1},a_i)\)

其实如果你想要更多个,也可以算出来下界,每次新算一条边的贡献,记录上一条变的流量为 \(cur\),则当前这条边的流量最小为 \(a_i- \min(cur,a_i)\)

可以证明,对于 dp 出来权值最小的方案,一定存在一种方案,直接输出就行了。

[USACO24FEB] Minimum Sum of Maximums P

先考虑分配好了如何快速计算贡献。

假定区间是 \([1,n]\),两端固定数值。要计算的就是 \(\min(\max\limits_{i=1}^{n-1}(a_i,a_{i+1}))\)

可以拆成 \(\sum\limits_{i=1}^{n-1}a_i + \sum\limits_{i=1}^{n-1}max(0,a_{i+1}-a_i)\)

前面是定值,后面可以看成是”爬升“距离和,下界肯定是 \(\max-\min\),且这个容易在 \([2,n]\) 升序排序的时候达成。

因此一段两头固定的区间的贡献是 \(\max-\min\)

之后就比较容易了,根据经典 trick 我们给每个区间安排的值域范围,要么包含,要么不交。

因此可以可化为树形结构,每个节点都是区间,我们做区间 dp。

\(f_{l,r,s}\) 表示我们使用了范围严格在 \([l,r]\) 内的值域,填入了 \(s\) 集合。

根据树形 dp,我们有如下几种转移:

  • 加一个根,需要根据长度是否为 1 分讨一下,贡献按上面的算。
  • 合并一个儿子,子集枚举即可,注意到我们需要满足的实际上是 \(l1 \le r1 < l2 \le r2\)\(r1-l1+1 \ge \sum len_{s1}, r2-l2+1 \ge \sum len_{s2}\),为了不枚举这么多,我们钦定一边是恰好满的,另一边就需要做一个二维前缀 \max 的东西来填充没有算入任何集合的元素。

Day2: 组合计数技巧

[FJOI2017] 矩阵填数

我们要算的实际上是:

枚举一种填数方案 \(P\)

\(\sum_{P}\prod_{i=1}^{n}[[maxv \le Lim_i]-[maxv \le Lim_i-1]]\)

后面那个直接 \(2^n\) 拆了,交换求和号,转为计数有多少个满足这个条件的方案数。

和思考容斥的柿子是完全相同的,这样考虑更形式化。

CF995F Cowmpany Cowmpensation

考虑组合的做法,插值太没技术含量了。

因为一共只有 \(n\) 种数,可以考虑求出来恰好使用了 \(i\) 种数的合法方案,然后组合数分配方案就行了。

求恰好转为钦定在哪个范围选,用第一类二项式反演即可。

Yet Another Permutation Problem

没听懂。

Distinct Multiples

考虑直接容斥的做法,枚举有多少个点对之间是相等关系,然后会缩成一些联通块,联通块的方案数是好算的。

很容易想到的优化是先确定联通快,再算容斥系数,注意到联通块之间是没有边的,因此是没有贡献系数的。

那我们要做的就是一个点集合,我们任意连边,边的的权值为 \(-1\),求这个点集能形成联通块的情况下,边权值的乘积。

直接上联通图容斥:\(g_s\) 是乱选边的权值之积,\(f_s\) 是保证联通的权值之积。

\(f_i=g_i-\sum\limits_{j}^{i-1}\binom{i-1}{j-1}f_j \times g_{i-j}\)

进一步的: \(g_s=\sum\limits_{k=0}^{\binom{|s|}{2}}\binom{\binom{|s|}{2}}{k}(-1)^{k}=[k=0]+[k=1]\)

所以 \(f_i=g_i-\binom{i-1}{i-2}f_{i-1}=[i=1]-(i-1) \times f_{i-1}\)

好厉害。

[THUPC 2024 决赛] 排列游戏

容易发现一个排列的代价为 \(\frac{\sum |p_i-i|}{2}\)

考虑如何快速刻画这个柿子,我们将 \(i\)\(p_i\) 连边,这样一个排列的代价是所有环上相邻两点编号绝对值之和。

直接用连续段 dp,扫描编号维,设 \(f_j\) 表示现在还有 \(j\) 个环没封口,然后你考虑插入一个数会发生啥:

  • 新建一个链
  • 成为自环
  • 接在链前面
  • 接在链后面
  • 合并两条链
  • 把链变成环

然后贡献可以用摩天大楼那题的思路,巧妙处理绝对值,然后同时注意到如果形成了 \(j\) 条链,那么至少贡献了 \(\frac{j\times (j-1)}{2}\) 次,因此第二维是 \(\sqrt{m}\) 级别的。

他妈的你倒是想到要把 \(i\)\(p_i\) 连边啊,一个一个填位置能做啥?

[POI 2015] CZA

\(p \le 2\) 暴力。

考虑 \(p=3\),很容易想到连续段插入 dp,关键在于怎么记录状态。

分讨,具体怎么分讨以后再说,题解 dx 做法没看懂。

[AGC022E] Median Replace

先考虑如何判定是否合法:

首先我们不会操作 \((1,1,1)\),然后剩下的操作就是形如 \((0,0,0)\)\((1,1,0)\) 以及 \((1,0,0)\)

剩下的我们采用遇到就消除的策略即可,维护一个栈,对于后两个操作,我们可以认为我们遇到一个 \((0,1)\) (注意必须先 0 再 1,因为 如果先 1 再 0 后面如果再加 0 用三个 0 消掉更好)就消掉,因为之后要消除的话剩下的数就是新加入的数,我们可以提前操作。

因此我们可以分析到

  • 我们剩下的一定是连续的 1 拼上连续的 0,都可以为空。
  • 0 的个数不会超过 2,1 的个数不会超过 2.

然后只需要记录二元组 \(([0,2],[0,2])\) 即可。

[PA 2019] Desant

Growing Sequences

\(d_i=a_i-2 \times a_{i-1}\)

容易发现 \(\{d\}\)\(\{a\}\) 双射,我们转为统计 \(d\),这样每项独立,但是对于 \(a_i \le m\) 的限制需要额外考虑。

只需要保证 \(a_n \le m\) 即可。

有递推式 \(a_i=2 \times a_{i-1} + d_i\)

根据这个可以写出 \(a_n=\sum\limits_{j=0}^{n}d_j \times 2^{n-j}\)

看到二进制,想到数位 dp。将 \(d_i\) 每一位拆了,然后这个 \(\times 2^{n-j}\) 实际上就是把 \(d_i\) 在二进制数位上进行一个平移。想象一个表格,每个位置有个 \(0/1\),每一行代表 \(d_i\) 的二进制拆分后平移,每一列代表最终求和式中这一位有几个 1。最终答案等价于这个表格的填数方案。

我们可以每次处理一个数位,但是需要考虑进位,按照从低位到高位的顺序容易处理进位问题,记录前面填的是 \(\le\) 还是 \(>\) 即可。

[ABC240G] Teleporting Takahashi

容易注意到一维是可以 \(O(1)\) 算的,然后两维你可以枚举分配给其中一维多少步,三步同理。

但是这样就爆炸了,如果能快速处理两维的话,我们仍然可以快速做。

一个神秘的思路:旋转坐标系 45°,这样原来的上下左右变成了 \((-1,-1),(-1,1),(1,-1),(1,1)\) 四种向量的走法。

然后这等价于你每次可以在每一维随便选 \(-1,1\) 然后走到 \((x+y,x-y)\) 即可,此时两维独立,直接套用一维的做法即可。

太神秘了。

[ARC163D] Sum of SCC

经典结论:竞赛图缩点之后是一条链,我们数所有的边(假设最后一个点向后有一条边)就是 scc 的个数。

缩点后的一条"边”存在,当切记当这条 "边" 前面所有点都向后面所有点连边,两部分点内部任意。

为了方便处理这个有多少条顺边(\(i \rightarrow j,i <j\)),我们按照编号扫描线。

记录 \(f_{i,j,k}\) 表示某条 “边” 前面有几个点,后面有几个点,有几条顺边。

转移枚举放到前面还是后面,并枚举向内部连的顺边个数即,复杂度 \(O(n^5)\)

一个优化是,既然我们每个集合内部连边都任意,那我们就不在转移的时候枚举,转移只记录两部分点之间的顺边个数。

那么最终的答案应该是:

\[Ans=\sum\limits_{x=0}^{n}\sum\limits_{k=0}^{m}f_{x,n-x,k}\sum\limits_{i=0}^{m-k}\binom{\binom{x}{2}}{i}\binom{\binom{n-x}{2}}{m-k-i} \]

复杂度仍然是 \(O(n^5)\),后面是个范德蒙德卷积,可以优化掉一个 \(m\)

总复杂度 \(O(n^4)\)

CF1824B2 LuoTianyi and the Floating Islands

\(k\) 是奇数答案显然是 \(1\)

带权重心的定义是所有儿子里面点的个数 \(\le \frac{k}{2}\)

因此如果你数点的话可以选择容斥,让某个儿子超过,然后需要高妙组合意义+推柿子才能做。

关于重心我们经典思路是变成边,容易注意到合法点一定构成联通块,因此数边的个数+1 就是答案,边当然是好数的,每个子树分配一半就好了。

CF2096H Wonderful XOR Problem

题解

Day3:

树上问题:

啥都没听懂。

[Ynoi2011] ODT

简单题,对于所有轻儿子搞一个数据结构维护就行了。

不会卡常。

树上邻域数颜色

研究了一万年,终于会了。

首先对每种颜色单独做,把每种颜色单独拉出来建虚树。

容易发现,一个点的邻域在虚树上的覆盖范围,一定是一个联通块,考虑点-边容斥。

一个问题是,虚树上有没有颜色的节点,如果我们只数点的话当然可以不算他们,但是如果数边的话就比较麻烦。

我们找到虚树上离他最近的点,记录距离为 \(f_u\),然后将所有与他相连的边长度增加 \(f_u\),这样可以认为它变成了一个有颜色点。

邻域包含点的条件是容易刻画的。

邻域包含边的条件是:

设两点间距离为 \(Len\),询问半径为 \(d\),则要求点距离 \(u,v\) 中点 \(z\) 不超过 \(\frac{d\times 2-Len}{2}\)

注意,由于上面边带权了,因此有可能这个 \(z\) 处于虚空边上,不过如果在虚空边上,包含这条边等价于包含产生虚空边的点。

现在相当于有 \(n\) 个点,然后要进行 $n $ 次静态邻域数点。

离线询问点分治,在线询问点分树即可。

复杂度 \(O(n \log n)-O(n \log^2 n)\)

常数分析:

  • 为了方便树边中间加点避免两点间距离是奇数:\(\times 2\)
  • 虚树:\(\times 2\)
  • 点边容斥:\(\times 2\)
  • 点分树/点分治子树容斥:\(\times 2\)

显而易见,这东西跑 \(2e5\) 都比较费劲。

做个梦呀做个梦

  • 做法 \(1\):

    结论:重心是深度最深的满足 \((tot-sz) \times 2 \le tot\) 的结点。

    从联通块根开始往下面走,依据上面这个东西。

    用重链剖分加速走路,需要对轻儿子额外维护一个数据结构(set),复杂度 \(\log^2 n\)

  • 做法 \(2\):

    结论:重心位于 Dfs 序上,第一个前缀和 \(\times 2 \ge tot\) 的结点的根链上。

    直接线段树二分找到这个点,然后在根链上二分即可。

天动万象

先考虑链上怎么做,相当于删掉一个点,一个后缀向前平移一位。

发现操作一次后叶子都没了,考虑使用这个性质做一些均摊的处理。

可使用链剖分将一个子树划分为不超过叶子个数个重链,如果我们能对一个重链快速操作,那就结束了。

发现和链上的情况基本相同,额外增加了一个轻边上的传递过程,容易用平衡树维护,线段树模拟平衡树应该也是可以的。

对于查询,整链是好处理的,只用一条链的查询形式是一个后缀,线段树查询即可。

CF566C Logistical Questions

结论:设 \(f_u\)\(u\) 到所有点的带权距离和,\(f_u\) 为凸函数,因此从重心向别的地方走权值和一定变大。

动态走路我们可以使用重链加速,静态走路我们可以使用点分治加速,这样相当于在只有 \(\log n\) 次单点求值,直接暴力就好了。

可能需要三度化。

[Ynoi2008] stcm

缺一分治的灵活应用。

考虑一个子树补就是扣掉 \([dfn_u,dfn_u+sz_u)\),直接考虑全局缺一分治。

缺一分治不仅在叶子是正确的,在分治树上的任何一个区间,都满足当前加入了除这个区间外的所有点。

对于跨过分治树中心的区间,由于是子树所以他们的关系一定是包含关系,按长度排序后暴力加点就行了。

字符串:

Suffix Automaton

先确定长度,转化为定长字典序第 \(k\) 大的最小 \(endpos\)

考虑按长度从后往前扫描线。

按照后缀排序的的顺序维护,然后长度减一会发生的事情:

  • 相邻两个合并;
  • 插入一个串;

注意到两个串合并之后,中间不可能再插入新的串。

然后你只需要找到,每个联通块内,开始位置最小的串就行了。

诗韵

第一开始想的是:直接爆做,然后有可能不是整段加,每个点额外维护一个数据结构,然后链加完了之后线段树二分更新,复杂度超神 \(log^2n\)

更好一点的思路是,预处理每个节点会被分裂的长度,然后把每个节点直接分裂了,此时就没有上面那种操蛋问题了。

然后 2log 有点过于麻烦了。

实际上一个节点成为满足条件的节点后就一直满足了,我们只需要求出来这个时间,然后将贡献柴刀每个节点上就好了。

最长公共后缀就是所有满足条件的结点的最长长度的最大值。

种类个数就是最长长度-父节点最长长度。

这个时间可以线段树合并+二分找到。

区间本质不同子串个数

感觉不是很难啊,是不是我想简单了?

对于每个字串,我们在他最后一次出现的时刻统计。

考虑 SAM 上的一个节点,代表长度为 \([l,r]\),假设在区间中出现的最后一个 endpos 为 \(t\),则会对左端点在 \([t-r+1,t-l+1]\) 的部分贡献 \(+1\),查询的时候查询 \([l,r]\) 内的贡献和。

扫右端点更新答案,注意到会改变根链的所有 \(t\),且都会变成相同的数值。而根链上的代表长度连续,利用颜色段均摊,可以直接做到 \(log^2n\)

[CTSC2010] 珠宝商

容易想到点分树完之后根号分治,小于根号的部分是容易的,直接在 \(SAM\) 上走路就行了。

考虑大于根号的部分。正着反着都建一个 \(SAM\)

然后需要支持在 \(SAM\) 上往前匹配。

然后直接做 endpos 的点积就可以了,复杂度 \(m \sqrt n\)

注意到需要容斥,容斥仍然需要采取根号分治,否则复杂度仍然爆炸。

小的直接暴力做就行了。

posted @ 2025-04-27 20:31  Richard_whr  阅读(105)  评论(4)    收藏  举报