Hey Gift:逃亡公路

做题记录。


P7077 [CSP-S2020] 函数调用

不像我能在 CSP 场上做出来的题。

注意到部分分有“函数调用构成一棵树”,所以不妨对于 3 函数向其每个调用的其他函数连边得到一个 DAG。

想象线段树 2 我们怎么维护懒标记的,你会发现我只需要维护每个位置上本身乘上多少,加法标记乘上多少就行了。

前者可以把所有调用到的 2 函数全部乘起来做到。

对于加法标记乘上多少,我需要考虑在这个加法标记后面有多少乘法操作。考虑拓扑排序。我先把最后要进行的操作序列捆成一个 3 操作,编号为 m+1。然后一般地,对于每个 3 操作我把操作顺序倒过来,算后面的对前面的贡献。

计算 muli 表示执行一次函数 i 可以导致全局乘多少,cnti 表示该函数被乘了多少次。注意到只有 1 函数的 cnt 才是有效的,其他函数的 cnt 没有意义。

mul 可以在一开始对 DAG 记忆化搜索一次得到。

在拓扑排序的过程中,我需要无视 2 函数,统计 1 函数的答案。对于 3 函数 u,我倒了操作序列后,我继续维护一下 uv 有向边指向的点 vcntv,具体来说垒一个 cntu 乘上前面所有(原来的后面)mul 的和然后加到 cntv 上去。

细节十分多。toposort 要入队所有入度为 0 的点但不要用 m+1 无法到达的点更新答案(要用来删边,不然可能有的点删不掉)。好好想想。Think twice, code once.

CF643F Bears and Juice

神题。

首先考虑这些熊想推测能已知的信息,有哪些熊睡了,以及这些熊在哪一天睡的。于是对于 i 天我们能够得到的不同信息总量就是 j=0min(p,n1)(nj)×ijj 枚举的是倒了几头熊,式子里面是倒下的熊的选法和倒下的日子的选法。

考虑是否能够建立某个信息与一种酒桶位置的对应关系。假设某一桶是酒,我们让一种方案对应它时,我们只安排需要倒下的熊在倒下那一天喝这桶,这样其他熊一定不会倒下,而又因为这一桶只会被用到这一次所以那些需要倒下的熊不会提前倒下,所以这一桶就对应且只对应了这种方案。

于是只要熊逆天聪明,明白映射关系,因为一一对应,答案上界可以碰到。组合数的实现细节可以看题解。

CF1872F Selling a Menagerie

水题,想到 iai 连边然后造出一个基环树森林式的结构,拓扑排序之后对环贪心就行了。

P6747 『MdOI R3』Teleport

考虑按位确定答案,答案中一位可以是 1 当且仅当这一位及其前面对和的贡献加上后面的最小贡献小于等于 m。还需要尝试一下是否可以是 0。如果两者都不行输出 1。后面的最小贡献可以每位都 O(log) 贪心算一遍,当然更好的是预处理再 O(1) 得到不过前者也过了。

注意要开 unsigned long long

P7914 [CSP-S 2021] 括号序列

非常 naive 地可以设出 dpl,r 表示 [l,r] 合法的方案数。

然后发现像 ()()() 这样的串因为断点太多所以会算重,不妨钦定转移断点时断点右边必须是一个包含型的串(即最左括号与最右括号匹配),这样就不会转移出错。

因此加一维 0/1 表示是否是包含型的串。

对于 ()***() 这种串的转移,由于不知道中间星号数量,所以枚举左串的断点,发现右串的起始点是连续的,所以前缀和优化即可。这里也需要注意去重。

注意前缀和和转移时的实现细节。

P1712 [NOI2016] 区间

妈的,读错题了。以为要最大莽了一大棵线段树。

比较厉害的方法。

需要让大减小最小,考虑双指针。对区间排序之后,若某个点覆盖次数 m 就把左边缩过来直到 <m

很明显覆盖次数最大的点一定只在线段端点上,维护所有线段端点的最大覆盖次数可以离散化后线段树维护区间最大值和区间加。注意动态开点线段树会被卡空间。

CF1872G Replace With Product

差一点点就想出来了。

首先可以发现我的区间端点一定选在某个非 1 的位置上,然后可以发现当区间内部乘积大于某个值时,无论如何扩展答案都不会变差(因为乘法太快了,加法太慢了),你就可以发现如果存在这样一个区间,答案就是左边第一个非 1 和右边第一个非 1 构成的区间;如果不存在这样一个区间那么最多就只有 log 个非 1 元素,这就可以暴力了。

想到这就没想了,因为不太懂“这个值”是什么。

再想一想,我先尝试一下左边第一个非 1 元素和右边第一个非 1 元素构成的区间,此时若内部乘积大于 2×109,我删掉左右一个元素答案起码缩小一半,也就是扣掉大于 109,但是最多只能少减 109,拿上这个元素不如不拿,所以此时答案就是这个区间。

否则非 1 元素最多只有 log22×109 个,这可以直接暴力,因为区间端点一定选在某个非 1 的位置上。

有点难受,差点想出。

UVA12141 Line Chart

上午的模拟赛题。

考虑维护 fi,j,0/1/2 表示在前 i 个点中删了 j 个,最后一个保留的点和上一个点的关系分别为 pi<pi1,pi>pi1 或者这个点为第一个点的方案数,转移容易得到并且容易发现离散化后权值树状数组优化。

然后需要去重,因为相同数值只能转移一次。注意到后出现的方案严格包含前面的所有方案,所以对于相同的数只转移目前最后一次出现的位置,将树状数组的单点加改为单点修改就完事了。

我的实现写丑了。可参考大佬题解。上午脑抽+急死我了没想出来去重拿了个破 30 分丢死人了。

CF1870D Prefix Purchase

水题。考虑贪心,先把最后一个最小的数给取了。

但是你发现有点问题,因为这样会剩下一些钱浪费掉,可以撤销一些取这个最小数的操作,去取等量的次小的数。

撤销操作后相当于在后面做子问题。假设目前剩下 c 的钱,目前的最小数为 x,后面次小的数为 y,假设撤销 z 次则必须满足 zx+czy,很明显不可能撤销之后后面取的次数能比撤销的次数还多。于是可以得到最大的 z。剩下的模拟即可。

CF1870E Another MEX Problem

没见过的东西。

有一个很容易想到的可行性 dp 是 dpi,j 表示拆分前 i 个元素得到 mex 异或值为 j 是否可行。

转移很显然但是直接做是 O(n3) 的,我们考虑优化。普通的转移优化都不好使,我们考虑有没有无用状态。

可以发现如果 mex[l,r]=mex[l+1,r]mex[l,r]=mex[l,r1],则这个区间是无效的。对于第一种情况,可以让前面多取一个数,则一定包含它能转移到的所有方案,所以没用。对于第二种情况,可以给后面多留出一个数,所以也一定包含它能转移到的所有方案,所以也没用。于是我们可以把这种区间去掉。

下面有一个结论是有用的区间只有 O(n) 个。

不妨设 al>ar,如果这个区间有用则 al 不能被删掉,那么就有 mex[l,r]>al>ar。然后你考虑在 r 后面所有满足 al>ar1r1,这种 ar1 因为比 al 小所以一定在 [l,r] 中出现过一次,也就是说删掉这个元素 ar1 前面还会有东西顶住他的位置,于是一定有 mex[l,r11]=mex[l,r1],这个 [l,r1] 区间是无效的。

所以对于每个位置都考虑它的右侧,可能有 al<aral>ar 两种情况。很明显 al=ar 没有意义,随便删一头都不会影响 mex 肯定是无效区间。综上,即便再算上一个元素的区间一共也就只有 O(n) 个有用区间。

可以 O(n2) 找出所有有用区间 [l,r],然后把它放进 l1vectorr 转移就是完事了。

[ABC082D] FT Robot

又差一点。

已经注意到了 T 个数为奇数时方向改变为原来的垂直方向,偶数时只能不变或者变为相反方向。然后也想到了用 bitset 优化可行性 dp。

可惜没有继续往下想了。

先把原点移动到第一段 F 结束。继续推第一个结论可以发现如果前面有奇数个 T 则是在 y 轴平行方向上下移动,反之在 x 轴平行方向左右移动。

很明显这两者是独立的,于是你可以得到它能够到达哪些 x 坐标,哪些 y 坐标。只要目标点的两个坐标都是可达的就完事了,不用维护整个二维平面上所有可达点。

写出转移可以发现是 dpi,jdpi1,j+w,dpi1,jw 类似的等等(w 是这个 T 后面连续 F 的数量),只要先滚动数组然后给 xy 轴都开上 bitset,转移时左移右移按位或一下就好了。

[ABC078D] ABS

比较诈骗的数据范围。

你发现这两个人无论如何反正拿到最后当中必须有个人拿 an1 或者 an 然后前面的策略一点用都没有。那么 X 的策略是,Y 如果想拿到 an2 以及之前的,就说明拿 an1an 没拿前面的好,就偏偏让他拿后面的这两个。如果他想拿或者被迫拿后面的这两个,X 可以这样构造强迫他拿其中一个:

  • X 一来就干掉 1n1,他只能拿 n
  • X 一来就干掉 1n2,这时候如果他选 n1 那么 X 只能选 n。如果这时候他选 n 说明 (an1,an) 的搭配对于他来说不够好,也就是说对于 X 来说更好。那么 X 就会采取第一种措施。

这两者的结果是一致的,所以 |anan1| 是答案的第一种情况。

另一种情况是不让 Y 拿,那么答案是 |anW|

注意特判 n=1

CF1067A Array Without Local Maximums

这水题啊。dpi,j,0/1/2 表示确定前 i 个数,第 i 个数为 j,第 i 个数与第 i1 个数的大小关系乱转移就行了。0,2 的转移可以用前缀和优化。注意会超空间所以滚动一下数组就完事了。

CF1858D Trees and Segments

先写了个巨长的伪算然后看了题解。

考虑枚举最长 0 是哪一段区间,假设枚举 [l,r],那么将 k 减掉 [l,r]1 的个数得到 k(需要用 10),相当于在 [1,l)(r,n] 中的一个用 k01 使得连续 1 的长度最大。

问题转变为求一个前缀区间或后缀区间允许做若干次 01,使得连续 1 最长。

剩下这部分就是自己想的了。考虑 dpi,j 表示从 i 开头向右做不超过 j 次操作的最长连续 1pdi,j 表示从 i 开头向左做不超过 j 次操作的最长连续 1,这两者都可以轻易先递推出来恰好 j 次,再前缀 max 得到。

然后对 dp 做后缀 max,对 pd 做后缀 min,枚举的时候在两边查询就完事了。

注意多测清空和转移顺序,吃了一堆罚时。

CF1856D More Wrong

想过分治,但是快速弃掉了。唉。

容易想到如果区间 [l,r] 的逆序对数量等于区间 [l,r+1] 的逆序对数量,则 r+1max[l,r+1]

那么又可以得出一个结论(题解称作区间最大值判定定理),若 x 是区间 [l,r] 中最后一个满足 [l,x1] 逆序对数量等于 [l,x] 逆序对数量的元素,则 x=max[l,r]

考虑分治,假设 x1 位置上的元素为 [l,m] 最大值,x2 位置上的元素为 [m+1,r] 最大值,则 [l,r] 的最大值必然是这两者中其中一个位置上的,检验 x2 即可。

那么询问的代价为 T(n)=2T(n2)+O(n2),根据主定理这是 n2 级别的。由于每次合并需要 2 次,代价复杂度自带一个 2,因此是 4 倍常数,可以通过。

CF1879E Interactive Game with Coloring

一个很 naive 的想法是每一层染一种颜色,然后可以发现把颜色模掉 3 之后也能判断儿子父亲关系,所以颜色数上界就是 3,考虑答案是否可以为 1,2

1 的情况是显然的菊花图。

2 的情况,我们仍然考虑交替染色,发现恶心的情况是 2 度点,0,1 各占一个,无法判断儿子父亲,所以我们要避免这种情况。可以发现如果有两个深度奇偶性不同的 2 度点就区分不了了,所以只要不是这种情况就可以用 2 的情况。

但是这样还是不对。你会发现除了第一层边以外的边都是锁死了的,也就是说当我钦定某个根到根儿子的边的颜色时,该儿子整个子树的颜色都被确定了。这也就说明我们可以通过调整根向根的儿子的边的黑白颜色,改变整个根儿子子树的黑白颜色,也就能改变两个分属不同根儿子子树的深度奇偶性不同的 2 度点的染色情况。我们只要通过调整根儿子子树的染色方案,让所有 2 度点向上的边一样就完事了。

如果做不到,则颜色数就是 3

这玩意赛时急死人的恶心程度绝对不亚于 Fish Gragh 和 Flower Gragh。

CF1882D Tree XOR

菜了,这玩意不会做。该看样例解释的。

考虑根在 1 的时候怎么做。可以发现选择根节点 1 操作是无效操作,整个树全部异或上一个值等于啥也没干。所以最终情况应该是所有点的权值 =a1

那么这又说明一件事,每个子树都需要先异或成和该子树根节点权值一样的值才能继续操作。否则这个子树内有至少两个不一样的权值,总要异或成一样的,或者不是根节点的权值,那么一定做了无效操作。

那么每一个点 x 在向上合并的过程中,每次计算答案都要贡献上自己的 1,而它的权值会 axafaxafafax,那么所有点的总权值就是 i=1njajafaj,其中 j 是一个在 i1 路径上但不在 1 处的点。

根据 ajafaj 形式转而考虑每一条边可以发现答案其实可以写成 i=2n(aiafai)×sizi,因为每个点子树内的点都一定会贡献这个点到其父亲的边。这个可以直接换根做了。

P1979 [NOIP2013 提高组] 华容道

首先考虑暴力怎么写,把整个棋盘塞进去也太憨了,可以发现我只关心棋盘上的空格和需要移动的格子在哪里,其他格子肯定都是确定的障碍物或者辅助棋子,所以把这两个位置塞进状态就可以了。

然而这样做只能拿 70 分,不做出来这个题我 AK 不了提高组,怎么办呢???

你发现这个把空格也塞进去的操作也很憨,因为你第一步操作肯定是把空格移动到移动格旁边,然后接下来只会有两种操作:

  • 交换空格和移动格。
  • 将空格从移动格的一边移动到另一边。

把空格移动到移动格旁边的步数,可以直接 BFS 一下空格到移动格旁边但不经过移动格的距离,这是 O(qnm) 的。

然后很容易想到空格在移动格旁边只有 4 种状态,所以状态数是 4nm 大小的。对于第一种操作,直接在状态之间连接边权为 1 的边;对于第二种操作,可以预先 BFS 出从 (i,j) 的某一边出发,不经过 (i,j) 到达某一边的需要步数,把这个距离当做边权连接第二种状态就行了。第二种操作需要预先 BFS 4nm 次,复杂度为 O((nm)2)

连完边之后直接跑一次 Dij 就完事了。边数和点数都是 O(nm) 的所以没问题。注意实现细节中不要连出任何多余的边,需要判断目标和起点是不是障碍物,否则都有可能节外生枝。

CF1878F Vasilije Loves Number Theory

结论是,如果两个数 a,b 互质,那么 d(ab)=d(a)d(b)

然后数 a 的因数个数是将 a 唯一分解之后,每一个质因数指数加一之积。

那么用 map 维护 n 的唯一分解后的质数桶,还原时直接将原 map 赋值到操作 map 即可。乘法时合并 map,维护 d(n),然后对 d(n) 唯一分解,检验其是否被 n 整除即可。没有必要维护 n,因为维护了 n 的唯一分解质数桶,判断整除是 naive 的。

[AGC032B] Balanced Neighbors

不连通图的补图必然连通。所以先构造出一个不连通图满足每个点自己的编号加上自己所连的点的编号的和是相等的,它的补图就可以满足原条件。因为每个点都将连到除了自己和自己在原图中所连点的点,由于这些被排开部分的和是相等的,总和也是一定的,一定能做到补图中每个点所连到的点的和相等。

[ARC151B] A < AP

枚举 i,钦定前 i1 个元素和他们的置换元素都相等,而这个位置上的数,如果还没有被钦定和自己的置换元素相等(包括自己的置换元素是自己也不行),考虑使得它比置换元素小。

用并查集合并相等的数,假设此时有 cnt 个连通块。由于 ai 还没有被钦定和 api 相等,所以他们分属不同连通块。其他的 cnt2 个连通块的赋值方法总数显然是 mcnt2,而这两个连通块,必须满足 ai 的连通块的值小于 api 的连通块的值。很明显随着 api 连通块的值 1mai 连通块的取值可能数量 0m1,直接等差数列求和得到这两块的赋值方案为 m(m1)2。与其他块的赋值方法乘起来即可得到 i 处的贡献。

对所有 i 位置的方案数求和即可。

[AGC007B] Construct Sequences

考虑先给 api 放上 i,为了使得 a 递增,我们考虑使 aiai+20005×i 抵消影响。

注意到 ai 加上的数是一个递增序列,则他们的相反数为递减序列。非常精妙地,所以我们先构造 bii×20005,这样 api+bpi=i,能够满足我们的要求。

然而这样 b 中有负数,直接给每一项狂暴加 200052 即可调整为 1 的数、不违背大小关系,并且能够满足在上界 109 以内。

[ABC304F] Shift Table

考虑顺序枚举 m,每次扫一遍 s 得到哪些位置必须是 #。由于 maxn=1105d(n)=128 所以复杂度正确。

容易想到每个不需要是 # 的位置的可能都是 2 种,所以统计一下有多少个位置不必须是 #,算一个 2 的幂即可。然而这样显然会算重。

考虑去重。可以发现 m=1 的答案会在 m=2,3,4, 时重复计入,m=2 的答案会在 m=4,6,8, 时重复计入,m=k 的答案会在 m=2k,3k,4k, 重复计入,直接给每个位置开个需要减掉的方案数量的标记,算完一个的答案之后向后维护标记即可。由调和级数知复杂度是 O(nlogn) 的。

[ARC150C] Path and Subsequence

一眼看上去完全不会做,会想删掉这 k 种点权的点,或者考虑分成两半分治。然而删点复杂度和正确性显然比较扯(因为可能存在一种路径要走这 k 种点,只是顺序乱了),图不一定会分成两半。

考虑转换问题,题目要求所有的路径都必须满足 B 是一个子序列,那么我们考虑一条满足 B 不是其子序列的路径,它与 B 的配对长度肯定小于 k。所以我们考虑算出 1 到每个节点与 B 的最小配对长度。很明显这里的配对指的是从头开始一个一个配能够配到 B 数组的什么位置,不是最长公共子序列。

当我们检验一个序列是否是另一个序列的子序列时,一定是能够配上就配上,这个结论容易证明。

于是我们构造了数组 disi 表示 1i 的所有路径中与 B 的最小配对长度,由于子序列是能够配上就配上,我们在所有可以配上的情况中取 dis 最小值转移即可。

最后检验 disn<k 即可。

做法比较牛。需要多理解。

#539. 「LibreOJ NOIP Round #1」旅游路线

一篇题解

首先可以发现,使用所有加油站的目的都是将油量变为一个输入时就能确定的数 min(ci,C)

考试时想到了处理 fi,j 表示从 i 处满油出发,消耗恰巧 j 元钱可以走的最大路程,给这玩意垒一个前缀 max,改为消耗最多 j 元可以走的最大路程,这样我们二分一下第一个 d 的位置(也就是钱数)就可以 log 回答查询了。(注意恰巧 j 元时是没有单调性的,不一定花的钱越多路程就越大。)

转移很容易,fu,j+pumax(fv,j+gu,v,fu,j+pu),其中 gu,v 表示在 u 处满油,到达 v 的最长路,相当于限定了 uv 的边数不能超过 min(cu,C)。这里 u,v 显然可以不需要有一条边连起来,只要有一条路径就行了。

转移 f 可以在 O(n4) 以内解决(因为钱数的级别是 O(n2) 的),考虑怎么搞出来 g

考试时只会强行全源 SPFA,然而复杂度达到了惊人的 O(n3m),无法通过。还干了一件很蠢的事情,就是把转移 f 也变成了最长路问题,又搞了个 O(n5) 的全源 SPFA……

下面介绍神奇的转移 g,是倍增 Floyd 的 trick。hi,j,k 表示使用最多 2i 条边下 jk 的最长路。那么按照正常 Floyd 一样在最外面枚举一个 p,转移 hi,j,kmax(hi1,j,k,hi1,j,p+hi1,p,k) 就行了。很明显两个边数为小于等于 2i1 的最长路拼在一起就是一个小于等于 2i 的最长路,当然由于表示的是最多 2i 条边所以也可以不拼。

把这东西拼起来变成 g 就行了。准确来说,如果限定的边数(即 min(cu,C))的二进制上 i 位是 1,那么对于每一个 gu,v 都去找到所有 v 满足用 2i 步做 vv 是可行的,然后转移 gu,vmax(gu,v+hi,v,v)。最后当限定的边数的每一位二进制都被满足时,g 就转移出来了。正确性类似数位 dp,因为每一位上增加的边数都不超过该位表示的数,所以总的边数也不会超过限定的边数。

剩下的事情就好办了。

P7453 [THUSCH2017] 大魔法师

矩阵乘法维护懒标记板子题。

你发现这种轮换加法用朴素线段树很难搞,这时候就应当搬出矩阵乘法维护懒标记。矩阵乘法维护懒标记的目的是类似递推地,将加法或者其他运算转换成矩乘形式,由于矩乘具有结合律,pushdown 时直接让左右儿子懒标记和左右儿子维护的值组成的矩阵都乘上矩阵做到下传,然后把本节点的矩阵改为矩阵的单位元即可(矩阵的单位元为一个主对角线为 1 其他全为 0 的方阵)。

这里要求左右儿子维护的值组成一个矩阵方便我们用矩阵形态的懒标记去更新它们。一般来说是一个一维向量(即一行的矩阵)。

注意矩阵乘法过程中的左行右列运算规则。即左边第 i 行与右边第 j 列配对相乘再求和得到答案矩阵的元素 (i,j)。为了方便结合律,一般需要控制两个矩阵的列数相等,这样乘出来的矩阵会和左边的矩阵形态相同(通常我们把维护变量的矩阵写在左边,右边的矩阵全是常数)。还需要注意,矩阵没有交换律,不要写错了。

在本题中,我们构造一个向量 [A,B,C,len] 维护在线段树的每一个节点上。其实这个 len 表示管理的区间长度并没有什么用,只是因为操作中出现了将和加上 v×len 和将和推平为 v×len 所以需要留下这一维方便我们矩乘时直接更新维护的变量。当然因此你需要保证 len 一直不变。

剩下的就不太难了,理解了上面的内容矩阵是非常好推的(只要构造一个矩阵满足维护的矩阵乘上这个操作矩阵之后,维护矩阵中的每个元素都和操作后的情况一致就行了),然后就变成裸的线段树维护区间乘法和区间求和,只是求和和乘法变成矩阵了,重载一下运算符还是非常好写的。不过此题需要较强的卡常。

CF11D A Simple Task

求无向图环数板题。

最初很容易想到 dpmask,i,j 表示路径上的点集为 maskij 路径的数量。但这样一个环会被算环长次,这肯定不好去重,我们考虑怎么让环的起点确定下来。

我们钦定每一个环的起点都是环上编号最小的点,这样每个环都只会被算两次,一次顺时针一次逆时针,这是好去重的。然后我们还可以直接去掉起点那一维,因为路径上点集中编号最小的点就是环的起点,我只关心环所以别的点是起点的情况不能被计入贡献,这就没必要记起点了。于是 dpmask,i 表示路径上的点集为 mask,起点为 lowbit(mask),目前终点为 j 的路径数量。计数 lowbit(mask)=j 的路径就行了。

转移时需要枚举状态以及下一次走哪个点。下一次走的点不能编号小于目前的起点(否则起点会被篡改为另一个点,另一个点的情况就算多了)。如果下一次走的点已经出现过,则不能再走(但是如果是起点则计入答案)。

最后会出现一个环顺逆时针被统计两次,以及“二元环”这种东西,需要去掉。

CF1178F1 Short Colorful Strip

菜了,这题又没想出来。

我们考虑区间 dp,dpl,r 表示区间 [l,r] 被涂好的方案数。因为 n=m 所以 [l,r] 中一定有且仅有一个位置是整个区间的最小值,那么我第一次要涂一段包含这个位置的连续区间,考虑枚举这个连续区间为 [i,j],这个最小值位置为 p。容易发现问题相当于在 [l,i1],[i,p1],[p+1,j],[j+1,r] 解决子问题,因为这些段里面目前涂上的背景颜色都是一样的(都是 c_p),并且段里面不可能存在一个颜色和背景颜色一样,本质上和原问题没有区别。把这四段的答案乘起来就是这种 [i,j] 对答案的贡献。对于每一个包含 p[i,j] 都计算一次就好了。

此时复杂度是 O(n4),但是可以发现 p 两侧是独立的,所以算出 [l,p1] 的总方案数和 [p+1,r] 的总方案数乘起来也可以得到 [l,r] 的总方案数。当然也可以写出转移的和式然后代数方法。

CF53E Dead Ends

一眼状压。

首先有一个很垃圾的状压是 dpmask 表示当已经选的点的点集是 mask 时,得到合法生成树的方案数,然而你发现这样无法去满足叶子数量的条件。于是又会有一个想法是增加一维表示已经有的叶子的数量,然而又可以发现即便加上了这么一维,在新加入一个点进生成树时无法判断插在的那个点是叶子还是普通点,也就无法更新叶子总数。

所以只能将状压改为三进制状压,dpmask 还是表示状态为 mask 时得到合法生成树的方案数,mask 的第 i 位为 0 表示该点未选,为 1 表示该点已选且是叶子,为 2 表示该点已选且不是叶子。转移是显然的,只需要枚举一个点集中的点和一个未在点集中的点,若他们在原图上有边,即可加入那个点集外的点,当然可能需要更新它插在的那个点集中的点的状态。注意,不能将 1,2 表示的状态反过来,否则可能出现大的 mask 向小的 mask 转移,不方便。

还需要注意,对于每一个状态 mask,可以发现构成它的方案会算重。因为最后插进点集的点的可能是任意一个叶子,但得到的树都是这个 mask 表示的树,所以说在使用 mask 转移时要除以一个 mask 中的叶子数量才是它真正的方案数。

注意在使用的时候必须除掉,否则可能重复方案越堆越多,不能在最后统一除。

CF906C Party

可以发现,如果我选择完全图上一个点,可以把它相连的所有点都合并入这个完全图。可以证明,存在一种次数最少的方法使得最开始从一个完全图开始操作,逐渐合并其他点最终使得整个图成为完全图。因为合并两个完全图的过程完全可以分为先构建两个完全图,在选择相连点使其合并,但如果先选择相连点,再一一倒着做构建其中某个完全图的选择,就能把合并完全图变成从一个完全图启动去合并其他点。

所以直接状压,压掉在同个完全图的点集。dpmask 表示 mask 中的点都在一个完全图内的最小操作次数。转移容易。

LHX Cup T2 无限酒店

考虑没有询问 3 时怎么做。赛时已经发现,每一个团队的房间编号一定构成一个等差数列,所以维护首项和公差就已经可以做询问 2 了,操作 1 可以看成是全局乘和全局加,维护两个标记,在插入新团队时将初值就设置成抵消掉标记的就行了(比如逆元或者相反数)。

下面考虑询问 3。我们把它之前的插入操作按无限人团队和有限人团队分段,将连续的有限人团队加入合并成一块,这样每两块都会有一块无限人的团队。

接下来我们倒着遍历操作,如果插入了一个有限人的团队块,则尝试减掉这个团队块的总人数,减不掉就直接在该块内二分。如果插入了一个无限人的团队块,则尝试除以二让它回到它原本的位置,除不掉很明显说明这个位置是奇数,那么也就是新加的这个团队。

因为每次除以二,所以最多遍历 2logV 个段,复杂度可以接受。注意实现比较麻烦。

Bot Cup T2 多项式取模

(咕咕咕)

Qingbai Cup T3 有没有人来告诉我

考虑 dp,发现 m 特别巨大,考虑矩阵快速幂。

那个条件一股 sky walker 味,化一下就知道本质就是大的数总是小的数的倍数。那么很容易想到,为了凑出 m,先选择大的数然后再选择小的数,从大到小依次选择,能选就选,因为一定存在一个 1 所以总是能够填满。

(咕咕咕)

Milo Cup T1 密码锁

赛时想出了 dp 状态以为复杂度特别高,遂弃。Chery 说算 dp 时间复杂度是否可过的时候一定要把常数塞进去。

很容易想到设 dpl,r,x 表示消掉了 [l,r] 的密码锁,在 r+1 位置残留一个 x 大小的魔力的最小总花费。转移容易想到枚举两个断点,dpl,r,xminlmid<r,0kx{dpl,mid,k+dpmid+1,r,xk,dpl,r,x}。还需要有两个特殊转移:

{dpl,r,x2dpl,r1,xarxdpl,r,ar2dpl,r1,xar>x

意义显然,前者将残留的魔力进行了向后的移动,后者在这个锁上进行了一次新的施法。

初值为 dpi,i,ai2=ai。答案显然。

复杂度为 O(n3V2),虽然看起来直逼 7e9 但是区间 dp 常数非常小,因此能过。

Milo Cup T2 棋局

咕咕咕

Milo Cup T3 通道

贪心,考虑每个儿子子树内都还有 0,1,2 个可用的叶子。我们称呼他们为 0,1,2 儿子子树。

1 儿子子树大于 2 个,显然他们必然会先两两匹配(无论选择哪条链向上,若还剩两条链,他们必然会匹配上)。最后留下 0,1,21 儿子子树。

2 叶子子树大于 1 个,显然他们必然会两两匹配(同理,他们也必然会匹配上),最后留下 0,12 儿子子树。

为什么有两个 1 儿子子树的时候我们不合并? 考虑一棵节点数为 7 的满二叉树,那么答案显然为 1,但如果像上面那种方法合并的话就会出现答案为 2。这是因为向上留下 2 个可用叶子的时候,因为这两个叶子不匹配,则必然有一个叶子要占用掉到父亲的边去和外面的叶子匹配,这样可以去试图杀掉另一个叶子,一对这样的 2 子树合并在一起的时候只用 1 根链而不是两根链。

完成上面的匹配过程后,如果还剩下 12 儿子子树,且剩下两个 1 儿子子树,为了杀死 2 儿子子树里的叶子,则将其中一个 12 合并在一起。

为什么这种情况有两个 1 儿子子树的时候我们也不合并? 对于刚刚那种情况,我们会留下一个 1 向上继续匹配,这种 1 如果匹配不到别的链是不会增加链数的。而合并了之后由于 2 儿子子树中必然有一根链要向上,所以也会留下一个 1 继续向上匹配,但是匹配失败了会增加链数,所以一定不够好。
hack input:
8
2 5
3 5
5 7
5 6
4 6
8 5
6 1
hack answer:
2
hacked output:
3

如果还剩下 12 儿子子树,且剩下 11 儿子子树,那么将那个 2 叶子儿子子树中的一条链提上来,杀死其中的一个儿子,这样这个点就又成了一个 2 叶子的子树,比直接匹配掉要好。

为什么会更好? 假设我们在这个地方构成了链,那么答案将直接加 1,这个 2 儿子子树相当于贡献了 1 整条链,但是若把它提上去,答案不会变差(它不可能贡献超过 1 条链吧)。而如果它和别的 2 合在一起,那么它相当于贡献半条链,这会更好。

如果某种儿子子树一个都不剩,则把另一种直接提上去就完事了。如果都没有就不管了。

实现时为了避免根的度为 1,取一个度不为 1 的根而非直接取 1

很精妙的贪心题!

Cary Cup T1 答案补全

这种东西一看写高精度复杂度就不对,推矩阵又推不出来,要是如果有取模就很方便了。所以我们考虑哈希一类的东西。

首先你可以算一个 n 的错排数 DnP 取模的值,我们不知道 P 是多少。下面考虑我们怎么搞出一个信息,使得我们可以复原问号那一段,显然这个信息要排除掉其他所有数位,因此考虑减法,把 ? 设置成 0 再求一个输入串对 P 取模的值,两个数一减就可以得到中间 ? 构成的数乘 10kP 取模的值,其中 k 表示最后一个 ? 后面还有多少个数位。

那么我们直接把中间那个数乘上 k101(modP) 就行了。当然 P 需要大于 10l 否则中间那个数可能有多个但我们不知道模出来的是哪一个,109+7 就是一个很好的模数。

Bonus:如果 ? 不连续怎么做?

做法 by StayAlone and Bot 发现 ? 数量不是很多,要是可以爆搜中间那一段是什么东西,得到整个数,然后就相当于判断一下这个数能不能和所有 Dn,n(1,105] 匹配就行了,这很容易想到哈希。 然后我们发现问号有九个还是不能直接搜,但是 mitm 之后就可以搜了!我们把左边那半搜出来,然后算出左边那半的哈希值,把右边那半搜出来,算出右边那半的哈希值,给左边那半乘上你哈希需要乘的进制数的幂一类的,相当于解 x+yDn(modP),枚举 x 右边开桶或者 map 就可以了。SA 实验得到双哈希就能保对了。

Cary Cup T2 对角线

赛时使用 OEIS 通过。

考虑容斥,设 fi 表示有至少 i 个行不合法,j 个列全部合法的方案数。

很容易写出容斥 ans=i=0n(1)ifi,考虑如何计数 fi

首先显然可以有 (ni) 个选出不合法行的方案。

i 个行不合法,说明这 i 个行上的数全是 1,因为我们钦定了主对角线上的数都是 1,而这些行不合法说明它们上的数都是一样的。对于这 i 行,它们对应的列还有 ni 个空位,因为主对角线上那个 1 被反复钦定了,而这些空位随便填只要不填出全 1 就是合法的,所以单列方案数为 cni1,共 i 列所以总方案数为 (cni1)i;对于另外 ni 行,它们主对角线上的 1 没有被反复钦定,所以空位只有 ni1 个,方案数为 (cni11)ni。综上,fi=(ni)(cni1)i(cni11)ni

代回容斥式子就是 ans=i=0n1(1)i(ni)(cni1)i(cni11)ni,很明显 i=n 是没有贡献的,所以为了防止 cni1 意义不明我们把上界调小。

Cary Cup T4 分身术

看上去很像廊桥分配!但是你发现这里不一定是能选就选,先到先得。(所以廊桥分配其实是加速模拟题,这是贪心题。)

你发现你要选一个区间需要考虑上一个区间的右端点,所以首先有一个很显然的贪心(你也可以通过想办法把它搞成廊桥分配想到),将所有区间按右端点从小到大排序,然后你维护一个 k 个人所有的选法的最后一个区间。现在你考虑新加入一个区间,你先找到所有能加进去的位置,若有多个,选择右端点最大的那一个。

为什么这是对的? 能选就选:如果不选他,因为右端点单调不递减,所以最多再放进来一个区间且右端点不会比它小,所以不如放这个区间。

选择右端点最大的:给后面留下右端点尽量靠左的显然不劣。

于是你就有了一个非常普及组的 O(nmlogm) 做法,因为 log 不满所以能过 80。难道真的就不能 AK 了吗????

你考虑一个事情那就是每一个点上都最多有 k 个区间,所以只要一个点上少于 k 个区间,这里就有空闲的位置。所以你考虑上面那个做法的另一种维护方式。放入一个区间时进行区间加,然后找出值为 k 的最右边的一个位置,尝试在这个位置右边放入这个新区间就行了。

为什么一定是最右边? 考虑有一个左边的 k 位置可以放这个区间进去,由于按照右端点排序,这个区间一定比去顶右边那个 k 的区间先放进去,所以不可能存在这种情况。

好,这有什么用呢?

还记得廊桥分配第一篇 SF 的题解的结论吗?在有无限个廊桥的时候,如果一架飞机停在廊桥 k,那么廊桥数量 k 时它都会停在 k

廊桥分配,启动!

这里相似的结论是如果一个区间在 k 个人时会被选中,那么这个区间在 k+1 个人时也会被选中。虽然这肯定比廊桥分配难证多了,EI 也没证出来,但是这是对的而且可猜。

知道这个了有什么用呢?

你先枚举一下选多少个人,对于每一次你找到最靠右的那个 =k 的位置,肯定贪心的选一个 l 在它右边且 r 最小的区间,放进去接着做就行了,放不了就退出输出答案。维护一下每个 l 上的最小 r 和最小 r 位置建立权值线段树,这样复杂度就可以做到 O((m+n)logn),当然为了找出最小 r 位置进行线段树上二分可以做到好写一点点的 O((n+m)log2n)。写的时候可能需要在线段树上维护 set?比较难写,注意实现。

Key Cup T1 赤雪冥约

好皮。首先你考虑亚由怎么赢,如果某个子树操作一次就能赢那亚由肯定直接赢了对吧,而如果无法做到,则佑一可以直接在另一个位置填上 k,亚由这波策略直接报废。显然亚由如果在一个地方妄图调虎离山,佑一填一个地方亚由就会在另一个地方赢,那么另一个地方一定是一个填一次就能赢的,这是等价的。

所以问题转化为检验子树 mex 是否是 k,或者差一个数是 k,显然可以 DSU on tree 进行维护(考场上也就这么写的,然而重新发明 DSU on tree 实在写得太蠢了,调了 2h。建议背诵 OI-wiki 写法),当然也可以线段树合并、拍平树跑主席树、O(n2w) 暴力按位或 bitset、只把 k 的元素放进 set 做树上 set 启发式合并查询 size 检验……

Key Cup T2 铃绘空事

注意到 k 的值域很小,考虑容斥。很容易想到一个枚举子集的容斥,枚举一个关键点点集 S,容斥系数显然为 (1)|S|,然后算出必须经过这个点集中所有点的路径数就行了。

怎么算出必须经过这个点集中所有点的路径数呢?考虑先算出路径的顺序。首先你发现必然需要有一个点满足可以到达其他所有点,这样的点必须有且仅有一个作为起点,删除这个点之后必然又要有且仅有一个可以到达剩下所有点的点,下一步一定是走这个点。依此类推。所以你可以先 O(nm) 预处理出 DAG 上所有点的可达性,任意两点间的路径数,以及 sum1 表示所有入度为 0 的点到某个点的路径数,sum2 表示某个点到所有出度为 0 的点的路径数。然后你对于枚举的一个关键点子集,处理出里面每个点能够到达集合中多少个其他点,按照从大到小排序就能得到路径顺序,对于两个相邻点,答案会乘上他们之间的路径数。最后再乘上起点的 sum1 和终点的 sum2 即可。

这样就能做到 O(nm+q2kk2),当然非常跑不满,可以获得 65 分,不如 O(nq)

你发现复杂度瓶颈其实首先在算出路径顺序。你发现一个合法顺序当且仅当排序后前一个点总能到达后一个点,那么前一个点的拓扑序一定小于后一个点,所以把它们按照拓扑序从小到大排序就行了。很显然这个条件是必要的,一个路径顺序拓扑序一定满足这个条件。而它显然也是充分的,如果存在某个点满足拓扑序小于后面的点,那么它一定不可能从后面的点走过来,因为它不会比后面的点先被入队,这样也就证明了满足这个拓扑序顺序的路径顺序一定起码是正确的。而如果没有这样一条路径,只可能是因为某个相邻点不可达,或者是起点无法走到,终点无法走出,这些情况我们都会乘上 0,所以没有关系。

于是我们有了一个 O(nm+q2kklogk) 的做法。

然而这很憨。枚举子集很容易想到 dp,很容易设出 dpi,j 表示路径中最后一个点为 i 长度为 j 的方案数。转移即为 dpi,jdpk,j1×cntk,i,其中 k 是按照拓扑序排序之后在 i 前面的点,cntk,i 表示 ki 的路径数。初始值设置为 dpi,1=sum1i。最终每个 dpi,j 还要乘一个 sum2i。因为你知道 j 这一维表示路径长度也就是集合大小,还是可以容斥的。

于是我们有了一个 O(nm+qk3) 的做法。我赛时就写的这个做法,还是以时限一半时间碾过去了。赛时先想到了 dp 才想到了按拓扑序先排序才能正确转移,最后三个半小时的时候过了这题感觉很厉害。

接下来考虑一个 O(nm+qk2) 的做法。

我们发现上面那个 dp 已经没有优化空间了,考虑取缔子集容斥。我们只需要去掉所有经过关键点的路径,尝试钦定一条路径只在它拓扑序最小的那个关键点上被去掉,设一条到达某个关键点但不经过其他关键点的路径为 h。考虑从 sum1 当中去掉所有需要经过别的点到它的路径,当然还是按照拓扑序排序,那么显然 hxsum1xhy×cnt(y,x)yx 前面的一个点。

到达这个点之后就可以瞎走了,所以求和 hx×sum2x 就是所有经过关键点的路径总数,把这个东西从初始路径总数中扣掉就是答案了。这样我们就做到了 O(nm+qk2),还是太菜了啊!

posted @   Shunpower  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示