2023牛客暑期多校训练营赛题总结

前言

这篇笔记记录我在 2023 年牛客暑期多校训练营的 10 场比赛中遇到并认为值得记录的赛题题解以及我的思考和收获。

总览

笔记

【01C】Carrot Trees

题意

给定长度为 n ( n ≤ 1 0 6 ) n(n\le 10^6) n(n106) 的实数序列 a a a 和一个整数 k k k,初始时全为 0 0 0,对其进行 m ( m ≤ 2 × 1 0 5 ) m(m\le 2\times 10^5) m(m2×105) 次操作:

  1. 将区间 [ l , r ] [l,r] [l,r] 中所有元素加上 x k \dfrac{x}{k} kx
  2. 将区间 [ l , r ] [l,r] [l,r] a i ≥ 1 a_i\ge 1 ai1 的元素减去 1 1 1

求经过所有操作后所有元素共被减过多少次。

做法
  • 将所有数值乘 k k k,实数问题转化为整数问题。
  • 无视 a i ≥ 1 a_i\ge 1 ai1 的限制,每次操作 2 都将区间所有数减 k k k,这样最后 a i a_i ai 的历史最小值与 k k k 的差距就反映了它被多减去 k k k 的次数 c i c_i ci c i = ⌈ − min ⁡ a i k ⌉ c_i=\lceil \dfrac{-\min a_i}{k} \rceil ci=kminai,问题转化为历史最值问题。
  • 这里的历史最值问题可以在线或离线处理。离线方法:用线段树维护 a 1 a_1 a1 每一次操作后的值,这样整个大区间的最值就是 a 1 a_1 a1 的历史最值。然后对操作序列的后缀施加以 2 2 2 为左端点的操作或以 1 1 1 为右端点的操作,就可以将线段树维护的内容转换为 a 2 a_2 a2 每次操作后的值,以此类推,就可以求出每个 a i a_i ai 的历史最值。
  • 整个过程的时间复杂度为 O ( ( m + n ) log ⁡ m ) O((m+n)\log m) O((m+n)logm)
笔记
  • 这题的核心在于“多减的量由历史最小值反映”这一点。可以这样理解:将 a i a_i ai 的所有历史值看作一个序列,我们将使得 a i a_i ai 跌下 0 0 0 k k k 归还,相当于是对一个后缀进行区间加,而显然最后一次进行后缀加的位置一定是序列的最小值点。

  • 这个离线求历史最值的方法很巧妙。整个问题的模型实际上是一个三维空间,三个维度分别为数组下标、操作序号、元素数值。传统线段树一般在数组下标上维护元素数值,并随着操作序号变化;而这里转换了一下方向,在操作序号上维护元素数值,并随着数组下标变化。


【02F】Link with Chess Game

题意

有长宽高为 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n105) 的三维空间( 1 ≤ x , y , z ≤ n 1\le x,y,z\le n 1x,y,zn),初始有一个点在 ( x 0 , y 0 , z 0 ) (x_0,y_0,z_0) (x0,y0,z0)。两人轮流将点沿坐标轴方向移动一格,不能移动到已到达过的点或空间外,不能移动者输。求先手必胜还是后手必胜。

做法
  • 因为对于两个相邻的点, x ⊕ y ⊕ z x\oplus y\oplus z xyz 的奇偶性一定不同,因此所有点可以按照 x ⊕ y ⊕ z x\oplus y\oplus z xyz 被划分为两部,问题转换为二分图博弈问题。
  • n n n 为偶数时,所有最大匹配都包含所有点,因此先手必胜。
  • n n n 为奇数时,二分图奇数部点数更多,说明一定有奇数部点不在最大匹配上,尝试构造发现任何奇数部点都有可能不在最大匹配上,因此 x 0 ⊕ y 0 ⊕ z 0 x_0\oplus y_0\oplus z_0 x0y0z0 为奇数时后手胜,反之先手胜。
笔记
  • 第一次了解二分图博弈这个模型,感觉证明很妙。

    模型:在一个二分图上,两人轮流沿着无向边推动一个棋子,且不能到达曾经到达过的点,不能推者输。

    结论:若起始点一定在二分图的所有最大匹配上则先手必胜,反之后手必胜。

    证明:I. 所有最大匹配都包含起始点时,先手每轮都沿着匹配的边走,后手只要能移动就一定会到达下一个匹配点,因此先手必胜。因为若后手到达非匹配点,将棋子移动路径整体向后偏移一次,可以得到一个不包含起始点的最大匹配,与条件矛盾。II. 存在一个最大匹配不包含起始点时,先手第一次移动一定会到达这个最大匹配的一个匹配点,因此两级反转,后手必胜。因为若先手第一次移动可以到达一个非匹配点,则该点和起点可以加入匹配,得到一个更大的匹配,与条件矛盾。

    解法:通常做法是用 Dinic 算法跑两次最大流。建图时先不建起点的邻接边,跑第一次;然后加上起点的邻接边,跑第二次,看第二次的最大流是不是 0 0 0。若是 0 0 0 则说明存在不包含起点的最大匹配,反之则说明起点对于最大匹配是必不可少的。模板题:https://codeforces.com/gym/102832/problem/H 。


【02G】Link with Centrally Symmetric Strings

题意

给定长度为 n ( n ≤ 1 0 6 ) n(n\le10^6) n(n106) 的字符串。求它是否能拆分成若干个中心对称子串。

中心对称的定义:o / s / x / z 自身中心对称;b·q / d·p / u·n / o·o / s·s / x·x / z·z 相对中心对称。

做法
  • “中心对称串”可以看作是稍微修改了回文串的定义,其性质依然适用 Manacher 的推导,因此可以用 Manacher 算法预处理出每个位置的最长中心对称半径。
  • 考虑维护符合要求的前缀。假设前缀 p r e i pre_i prei 符合要求,以 i + 1 i+1 i+1 为起点的所有中心对称子串 s 1 , s 2 , . . . , s k ( ∣ s 1 ∣ < ∣ s 2 ∣ < . . . < ∣ s k ∣ ) s_1,s_2,...,s_k(|s_1|<|s_2|<...<|s_k|) s1,s2,...,sk(s1<s2<...<sk) 都可以用于转移,但这里的 s 2 , s 3 , . . . , s k s_2,s_3,...,s_k s2,s3,...,sk 一定都可以再拆分为若干中心对称子串,所以选择最短的回文子串 s 1 s_1 s1 转移一定是最优的。
  • 根据上述结论,只要从左到右枚举每个位置作为转移子串的对称中心,选最短的半径进行转移,一定就能找出所有符合要求的前缀。
  • 整个过程的时间复杂度为 O ( n ) O(n) O(n)
笔记
  • 这题的关键在于“同一起点的较长对称串可再分”这一结论。思考了一下,这个结论可以拓展到回文串,并且等价于——中心对称串/回文串都有“最小拆分唯一”的性质:将它们拆分为若干中心对称/回文串直到不可再分,得到的结果是唯一的(感觉是常见套路,证明思路也很常见)。因为前缀 p r e i pre_i prei 能转移到的所有前缀, p r e i + s 1 pre_i+s_1 prei+s1 也都能转移到。下面证明 s i ( i ≥ 2 ) s_i(i\ge 2) si(i2) 都可再分且再分后的第一块为 s 1 s_1 s1

    首先反证法证明 ∣ s 1 ∣ ≤ ⌊ ∣ s i ∣ 2 ⌋ |s_1|\le \lfloor\dfrac{|s_i|}{2}\rfloor s12si:假如 ∣ s 1 ∣ > ⌊ ∣ s i ∣ 2 ⌋ |s_1|>\lfloor\dfrac{|s_i|}{2}\rfloor s1>2si,可以设 s 1 = A + B s_1=A+B s1=A+B s i = A + B + A ′ s_i=A+B+A' si=A+B+A,由 s i s_i si 对称性, B B B 是对称的;又由 s 1 s_1 s1 对称性, s 1 s_1 s1 一定有一个更短的对称前缀 B ′ B' B,与条件不符,证毕。

    然后就可以假设 s i = s 1 + S + s 1 ′ s_i=s_1+S+s_1' si=s1+S+s1。由 s i s_i si s 1 s_1 s1 都是对称的,可得 s 1 s_1 s1 S S S s 1 ′ s_1' s1 都是对称的,原结论证毕。


【03B】Auspiciousness

题意

2 n ( n ≤ 300 ) 2n(n\le 300) 2n(n300) 张牌面数字分别为 1 , 2 , . . . , 2 n 1,2,...,2n 1,2,...,2n 的牌叠放成牌堆,完成以下流程:

  1. 从牌堆顶取走一张牌
  2. 若牌堆为空则结束流程,否则猜测牌堆顶数字是否大于上一张取走的牌,并从牌堆顶取走一张牌
  3. 若猜测正确则回到上一步继续,否则结束流程

依照“上一张牌不超过 n n n 则猜测牌堆顶大于上一张牌,否则猜测小于”的策略猜测。求对于所有可能的 ( 2 n ) ! (2n)! (2n)! 种牌堆叠放顺序,总共能取走的牌的数量之和。

做法
  • [ 1 , n ] [1,n] [1,n] 为小数, [ n + 1 , 2 n ] [n+1,2n] [n+1,2n] 为大数。考虑摸到牌的序列,一定是小数上升序列和大数下降序列的交替。
  • 设计 d p ( i , j , k ) dp(i,j,k) dp(i,j,k) 表示已经填了 i i i 个小数和 j j j 个大数,最后一段是小数/大数的方案数,进行动态规划。动态规划前需要预处理出组合数 C i j C_i^j Cij 的值以便于 O ( 1 ) O(1) O(1) 转移。
  • 对于每个位置 i i i,计算可以拿第 i i i 张牌的方案数,然后求和。答案为 ∑ i = 1 n ∑ x = 0 i − 1 ∑ k = 0 1 d p ( x , i − 1 − x , k ) ⋅ ( 2 n − i + 1 ) ! \sum_{i=1}^{n}\sum_{x=0}^{i-1}\sum_{k=0}^{1}dp(x,i-1-x,k)\cdot(2n-i+1)! i=1nx=0i1k=01dp(x,i1x,k)(2ni+1)!
  • 整个过程的时间复杂度为 O ( n 2 ) O(n^2) O(n2)
笔记
  • 这题的关键在于如何划分状态以及如何利用状态的值计数。通常动态规划时的想法是直接用 dp 值来表示能拿走的牌数,然后对所有 dp 值求和得到答案。但这样必须考虑每一种方案下,下一次猜测失败的情况数,且不方便进行状态转移;并且这样其实存在信息的冗余,因为每一个状态拿了几张牌的信息已经包含在前两维 i i i j j j 里了。题解不直接用 dp 值存答案,而是用它来存关键的中间量,很巧妙。

【04H】Merge the squares!

题意

有一个大小为 n × n ( n ≤ 1000 ) n\times n(n\le 1000) n×n(n1000) 的正方形,由 n 2 n^2 n2 1 × 1 1\times 1 1×1 的小正方形组成。每次操作可以选择 x ( 2 ≤ x ≤ 50 ) x(2\le x\le 50) x(2x50) 个完整的正方形并将它们原地组合成一个完整大正方形,求一种由 n 2 n^2 n2 个小正方形构造出完整 n × n n\times n n×n 大正方形的方案。

做法
  • 逆向思维,将组合过程逆向为拆解过程,问题转化为求将 n × n n\times n n×n 大正方形拆解为 1 × 1 1\times 1 1×1 小正方形的方案。
  • 预处理出将 i × i i\times i i×i 的大正方形左上角拆下一个 j × j j\times j j×j 的小正方形,右下角拆下一个 ( i − j ) × ( i − j ) (i-j)\times (i-j) (ij)×(ij) 的小正方形后得到的两个矩形最少可以拆解为多少个正方形,对于每个 i i i,找到一个结果不超过 48 的 j j j 作为遇到 i × i i\times i i×i 正方形时的拆解预案。
  • 利用预处理出的预案递归构造答案。
笔记
  • 这题没做出来该反思。传统构造题的构造方法一般都比较固定和巧妙(虽然这题也可以固定),但这题预处理并自动选择构造方案,比较出乎意料。对于大正方形直接拆成两个正方形和两个矩形然后直接辗转相减就一定有解也是没想到的一个点,或许应该多打表尝试一下的。
  • 这题还有一种神奇做法是每次拆解大正方形都固定选在黄金分割点,这样得到的矩形长宽是黄金比例的,辗转相减得到的正方形会比较少。对斐波那契和黄金分割这些了解不多,不太懂深层的原理。

【04L】We are the Lights

题意

有一个 n × m ( n , m ≤ 1 0 6 ) n\times m(n,m\le 10^6) n×m(n,m106) 的网格,每一格上有一个灯,初始时都是灭的。进行四种操作:

  1. 打开所有第 i i i 行的灯
  2. 关闭所有第 i i i 行的灯
  3. 打开所有第 i i i 列的灯
  4. 关闭所有第 i i i 列的灯

求经过 q ( q ≤ 1 0 6 ) q(q\le 10^6) q(q106) 次操作后亮着的灯的数量。

做法
  • 观察到重要性质:后继操作会覆盖前驱操作,灯的亮灭取决于最后一次对它进行的操作。
  • 逆向思维,逆着操作顺序从后往前考虑,同时记录每一行每一列是否操作过以及操作的类型。对于每一行,只需要考虑对该行的第一次操作:在第一次操作该行时,根据已经操作过的列情况计算这一行灯的贡献,最后将所有行贡献求和即得答案。
笔记
  • 签到题,比较简单,但是感觉比较妙,值得品味。值得一提的是做法里行和列的地位并不对等,要么是以列为条件计算行贡献,要么是以行为条件计算列贡献。
  • 还有一种类似的问题是每次翻转一行或一列。那样的话只需要记录每一行和每一列的翻转状态(0 或 1)了,并且每一行的情形最多就只有两种,因为列操作对每一行都是平等的,行与行之间的差别只与行操作有关(“行”、“列”互换亦然)。举例:https://ac.nowcoder.com/acm/contest/57357/D
  • 再补充一个行列操作的问题,是每次交换任意两行或任意两列的:https://ac.nowcoder.com/acm/contest/57362/K

【05C】Cheeeeeen the Cute Cat

题意

给定一张 2 n ( n ≤ 3000 ) 2n(n\le3000) 2n(n3000) 个点, n ( n − 1 ) 2 \dfrac{n(n-1)}{2} 2n(n1) 条边的二分图,保证:

  1. i i i i + 1 i+1 i+1 间没有连边
  2. i i i j + n j+n j+n 以及 i + n i+n i+n j j j 之间不同时有连边

求该二分图的最大匹配。

做法
  • 题目给的限定条件显然是为了将问题转换到一张竞赛图上: i i i j + n j+n j+n 的无向边对应竞赛图中 i i i j j j 的有向边。现在要求在保证每个点入度出度不超过 1 1 1 的情况下最多选多少条边。
  • 竞赛图一定存在一条哈密顿通路,所以答案至少为 n − 1 n-1 n1
  • 竞赛图中每个强连通分量一定存在一条哈密顿回路,所以当且仅当所有强连通分量大小都大于等于 3 时答案为 n n n
  • 求强连通分量时间复杂度 O ( n 2 ) O(n^2) O(n2)
笔记
  • 对竞赛图不太熟悉。这里记录一下竞赛图的相关知识。

    1. 竞赛图若存在环,则一定存在三元环

    2. 竞赛图一定含有哈密顿通路

      构造方法:每次加入一个点 x x x,设加入前的哈密顿通路起点终点分别为 S , T S,T S,T,若存在有向边 ( x , S ) (x,S) (x,S) ( T , x ) (T,x) (T,x),则直接将 x x x 从端点处加入通路;否则 S , T S,T S,T x x x 间的边一定是从 ( S , x ) (S,x) (S,x) ( x , T ) (x,T) (x,T),这样的话通路中一定有两个相邻的点 u , v u,v u,v 满足存在 ( u , x ) , ( x , v ) (u,x),(x,v) (u,x),(x,v),此时将 x x x 插入 u , v u,v u,v 间即可。

    3. 竞赛图中每个强连通分量一定存在一条哈密顿回路(构造方法类似上述增量构造)

    4. 兰道定理:定义比分序列 s s s 为所有点的出度构成的序列排序而成的序列。则比分序列合法当且仅当:

      ∀ k ∈ [ 1 , n ] \forall k \in [1,n] k[1,n], ∑ i = 1 k s i ≥ C k 2 \sum_{i=1}^{k}s_i\ge C_k^2 i=1ksiCk2,且 ∑ i = 1 n s i = C n 2 \sum_{i=1}^{n}s_i=C_n^2 i=1nsi=Cn2

      把出度换成入度也成立。

      理解:竞赛图缩点后形成一条链,其中离链头较近的 SCC 中点的出度一定严格大于离链头较远的 SCC 中点的出度,因此比分序列就是由从链尾起的每个 SCC 对应的连续区间连接而成的。当 ∑ i = 1 k s i ≥ C k 2 \sum_{i=1}^{k}s_i\ge C_k^2 i=1ksiCk2 取等时,说明前 k k k 个点没有连向其余点的边,即前 k k k 个点构成链尾若干个强连通分量。所以,兰道定理除了能判断比分序列合法性,还可以求强连通分量: ∑ i = 1 k s i ≥ C k 2 \sum_{i=1}^{k}s_i\ge C_k^2 i=1ksiCk2 取等处就是强连通分量的临界处。

    参考博客:https://www.cnblogs.com/xzzduang/p/17205687.html


【05E】Red and Blue and Green

题意

给定 m ( m ≤ 1 0 3 ) m(m\le 10^3) m(m103) 个约束,每个约束的形式为:区间 [ l , r ] [l,r] [l,r] 的逆序对奇偶性为 w w w。求一个长度为 n ( n ≤ 1 0 3 ) n(n\le 10^3) n(n103) 的排列,使其满足所有约束,或断定不存在这样的排列。

做法
  • 以区间的直接包含关系连接所有约束后,所有约束之间将会构成一个树形结构。
  • 改变区间奇偶性只需要交换任意两个元素一次。
  • 将有根树构造出来,对于一个初始为顺序的排列,在树上 dfs 的同时自下而上修改排列。针对一个约束节点进行修改时,首先对它所有子节点的逆序对奇偶性求和,判断它是否需要继续修改;要想在当前节点的区间中交换两个数同时不影响子孙节点的结果,只要保证交换的两个数不在同一个子节点区间中,并且在子节点区间中的数原本是最大(小)值,交换后还是最大(小)值。由于子节点区间不等于当前节点区间,且当前节点和子节点的区间 [ l , r ] [l,r] [l,r] 一定都是 [ l , r ] [l,r] [l,r] 的一个排列,一定能找到一对满足条件的数进行交换。假设当前区间为 [ L , R ] [L,R] [L,R],一个子节点区间为 [ l , r ] [l,r] [l,r],则选取 [ L , R ] [L,R] [L,R] 中位于 [ l , r ] [l,r] [l,r] 左侧的数与 [ l , r ] [l,r] [l,r] 中最小值交换或选取 [ L , R ] [L,R] [L,R] 中位于 [ l , r ] [l,r] [l,r] 右侧的数与 [ l , r ] [l,r] [l,r] 中的最大值交换即可。
笔记
  • 这题的关键有两点:区间之间是树形关系且可以自下而上修改、如何保证当前节点的修改不扰乱其他节点。自下而上修改可以保证当前节点的区间 [ l , r ] [l,r] [l,r] 一定是 [ l , r ] [l,r] [l,r] 的一个排列,这又使得 [ l , r ] [l,r] [l,r] 左侧的数一定比它们都小,右侧的数一定比它们都大,以至于一定能找到可交换的数。

【05I】The Yakumo Family

题意

给定一个长度为 n ( n ≤ 2 × 1 0 5 ) n(n\le 2\times10^5) n(n2×105) 的序列 a a a,求:

∑ 1 ≤ l 1 ≤ r 1 < l 2 ≤ r 2 < l 3 ≤ r 3 ≤ n X O R ( l 1 , r 1 ) ⋅ X O R ( l 2 , r 2 ) ⋅ X O R ( l 3 , r 3 ) \sum_{1\le l_1\le r_1<l_2\le r_2<l_3\le r_3\le n} XOR(l_1,r_1)\cdot XOR(l_2,r_2)\cdot XOR(l_3,r_3) 1l1r1<l2r2<l3r3nXOR(l1,r1)XOR(l2,r2)XOR(l3,r3)

其中 X O R ( l , r ) XOR(l,r) XOR(l,r) 表示 a l ⊕ a l + 1 ⊕ ⋯ ⊕ a r a_l\oplus a_{l+1}\oplus\cdots\oplus a_r alal+1ar

做法
  • 区间异或和可以通过端点的前缀异或得出,因此我们预处理出所有前缀异或和
  • 先求解 ∑ 1 ≤ l 1 ≤ r 1 ≤ i X O R ( l 1 , r 1 ) = ∑ b = 0 30 ∑ 1 ≤ l 1 ≤ r 1 ≤ i [ a l 1 , b ≠ a r 1 , b ] ⋅ 2 b \sum_{1\le l_1\le r_1\le i} XOR(l_1,r_1)=\sum_{b=0}^{30}\sum_{1\le l_1\le r_1\le i}[a_{l_1,b}\neq a_{r_1,b}]\cdot 2^b 1l1r1iXOR(l1,r1)=b=0301l1r1i[al1,b=ar1,b]2b:拆位后相当于计算前缀异或和上每个位置前面有多少位置在这一位上与它相反。记算出的结果为 s u m i = ∑ 1 ≤ l 1 ≤ r 1 ≤ i X O R ( l 1 , r 1 ) sum_i=\sum_{1\le l_1\le r_1\le i} XOR(l_1,r_1) sumi=1l1r1iXOR(l1,r1)
  • 再求解 ∑ 1 ≤ l 1 ≤ r 1 < l 2 ≤ r 2 ≤ i X O R ( l 1 , r 1 ) ⋅ X O R ( l 2 , r 2 ) = ∑ b = 0 30 ∑ 1 ≤ l 2 ≤ r 2 ≤ i s u m l 2 − 1 ⋅ [ a l 2 , b ≠ a r 2 , b ] ⋅ 2 b \sum_{1\le l_1\le r_1<l_2\le r_2\le i} XOR(l_1,r_1)\cdot XOR(l_2,r_2)=\sum_{b=0}^{30}\sum_{1\le l_2\le r_2\le i}sum_{l_2-1}\cdot [a_{l_2,b}\neq a_{r_2,b}]\cdot 2^b 1l1r1<l2r2iXOR(l1,r1)XOR(l2,r2)=b=0301l2r2isuml21[al2,b=ar2,b]2b:与上一步类似,但将“前面每个相反项 i i i 都贡献 1 1 1”的前缀和改为“前面每个相反项 i i i 都贡献 s u m i sum_i sumi” 的前缀和,算出结果 s u m i ′ = ∑ 1 ≤ l 1 ≤ r 1 < l 2 ≤ r 2 ≤ i X O R ( l 1 , r 1 ) ⋅ X O R ( l 2 , r 2 ) sum_i'=\sum_{1\le l_1\le r_1<l_2\le r_2\le i} XOR(l_1,r_1)\cdot XOR(l_2,r_2) sumi=1l1r1<l2r2iXOR(l1,r1)XOR(l2,r2)
  • 依照上述规律再套一层,将前缀和改为“前面每个相反项 i i i 都贡献 s u m i ′ sum_i' sumi”,算出最终结果 s u m n ′ ′ sum_n'' sumn′′
笔记
  • 这题的做法实际上是嵌套了三层配合拆位利用前缀和加速计算的过程。要求 n n n 项乘积的和,就要对 n − 1 n-1 n1 项乘积的前缀和进行求和。
  • 这种对一个好几项相乘的式子求和,并且式子中带位运算的问题很常见,再举一例:https://ac.nowcoder.com/acm/contest/57361/K 。这类问题经常需要拆位+前缀和的思想。拆位可以将位运算转换为 0 和 1 之间的简单逻辑,同时时间复杂度只提升 32 倍;而前缀和可以将复杂的枚举求和过程压缩为多次 O ( n ) O(n) O(n) 求和。这类问题的关键就在于如何设计前缀和以及如何利用前缀和计算答案,比较容易晕,可以先把式子化出来再想。

【06A】Tree

题意

给定一棵 n ( n ≤ 3000 ) n(n\le3000) n(n3000) 个点的树,包括每个点的颜色(黑或白),同时给出反转每个节点颜色的代价。定义一棵树的收益为所有黑白点对间路径边权最大值的和。求 收益 − 总代价 收益-总代价 收益总代价 的最大值。

做法
  • 路径最大边权问题,考虑构造 Kruskal 重构树,两节点的 LCA 即路径边权最大值。
  • 在 Kruskal 重构树上 dp:定义 d p ( i , j ) dp(i,j) dp(i,j) 表示在节点 i i i 的子树中有 j j j 个黑点时的最大答案,用类似于树上背包问题的方法进行转移。最终 max ⁡ i = 0 n d p ( r o o t , i ) \max_{i=0}^ndp(root, i) maxi=0ndp(root,i) 即为答案。
  • 整个过程的时间复杂度为 O ( n 2 ) O(n^2) O(n2)
笔记
  • 这题的关键在于判断出树上 dp 的时间复杂度为 O ( n 2 ) O(n^2) O(n2) 而不是 O ( n 3 ) O(n^3) O(n3)。这实际上是一个常见模型和结论:在二叉树上 dp 时,计算一个节点的 dp 值需要枚举左子树大小再嵌套枚举右子树大小,这个过程相当于枚举子树中 LCA 为当前节点的点对。而整棵树的所有点对只有 O ( n 2 ) O(n^2) O(n2) 对,因此时间复杂度只有 O ( n 2 ) O(n^2) O(n2)

【06H】traffic

题意

给定一个 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n105) 个点, n + 1 n+1 n+1 条边的无向连通图,图上每条边的边权都是 t t t 的一次函数,并且一次函数的斜率和截距都给定。分别求 t = 0 , 1 , 2 , . . . , T t=0,1,2,...,T t=0,1,2,...,T 时图的最小生成树边权和。

做法
  • 图的边数为 n + 1 n+1 n+1,因此得到最小生成树只需要删除图上两条边,这两条边一定在环上,分两种情况:
  1. 两个环没有公共边。此时只要在两个环上分别选一条最大的边删除
  2. 两个环有公共边,即存在两个点之间有三条链路,此时需要在这三条链路中选择两条链路,分别删除一条边
  • 进行 dfs 找到两个环或三条链路,然后用李超线段树维护边权函数的最值即可。
笔记
  • 这题的难点在于如何实现找环或找链路的代码,剩下的就是套个李超线段树模板。出题人提供了一个很好的方法:在 dfs 时会遇到两次返祖边,分别对应一个环,对第一个环所有边的标记加 x x x,对第二个环所有边的标记加 y y y。最终若为两个环,则两个环上的边权分别为 x , y x,y x,y;若为三条链路,则三条链路上的边权分别为 x , y , x + y x,y,x+y x,y,x+y。我觉得这个方法非常实用,可以举一反三。

  • 我对图论的常见方法太不熟悉了,这里列举一些最近用到的:

  1. 无向图找环:dfs,若某边到达访问过的点,则一定是返祖边,对应一个环。维护 dfs 栈,将栈中从当前点到祖先点的所有点打上环标记即可。
  2. 有向图找环:Tarjan 算法,dfs 找强连通分量,强连通分量中至少有一个环。
  3. 有向图两点间路径/一点所在环:dfs 的同时维护当前路径,遇到目标时直接返回。bfs 因为不能维护路径信息所以不行。
  4. 有向图找两个点集间的任一条路径:bfs 找到一个起点 s s s 和一个终点 t t t,再用 dfs 找 s s s t t t 的路径。

【06J】Even

题意

给定一个长度为 n ( n ≤ 1 0 4 ) n(n\le10^4) n(n104) 的数组 a a a 以及 q ( q ≤ 1 0 5 ) q(q\le10^5) q(q105) 个独立的询问,每个询问给出 l , r , k l,r,k l,r,k,假设进行 k k k 次以下操作:

  • 若数组在区间 [ l , r ] [l,r] [l,r] 中有正偶数,则将 [ l , r ] [l,r] [l,r] 中最大且最左的正偶数减半;否则,将 [ l , r ] [l,r] [l,r] 中最大且最左的数减半。

对每个询问,分别求操作后 [ l , r ] [l,r] [l,r] 中的最大值 max ⁡ i = l r a i \max_{i=l}^r a_i maxi=lrai

做法
  • 观察性质可以发现,整个询问分为两个阶段:
  1. 将所有偶数末尾的 0 0 0 抹去,直到只剩奇数
  2. 每次选出一个最大的奇数右移一位,然后不断抹去末尾所有的 0 0 0
  • 同时还可以发现,对每个数的操作最多进行 log ⁡ \log log 次,因此总操作数不超过 n log ⁡ n\log nlog 次。
  • 对于第一阶段,操作 k k k 次后的最大值等于第 k + 1 k+1 k+1 次操作的对象或最大的奇数。将对区间进行的所有操作的操作数列出来,一定是单调递减的。因此问题转换为求区间操作数集的第 k + 1 k+1 k+1 大。
  • 对于第二阶段,操作 k k k 次后的最大值等于第 k + 1 k+1 k+1 次操作的对象或第 k k k 次操作对象变为奇数后的下一个操作对象。将对区间进行的所有操作的操作数列出来,不一定单调递减,但我们可以将一个奇数产生的一系列后续偶数用该奇数替换,替换后一定是单调递减的。因此问题又转换为求区间操作数集的第 k + 1 k+1 k+1 大。
  • 求区间第 k k k 大用主席树实现,但除主席树模板之外还有一些细节要处理。
  • 操作序列长 O ( n log ⁡ ) O(n\log) O(nlog),每次插入可持久化线段树 O ( log ⁡ ) O(\log) O(log),每次询问求区间第 k k k O ( log ⁡ ) O(\log) O(log)。整个过程的时间复杂度为 O ( n log ⁡ 2 + q log ⁡ ) O(n\log^2+q\log) O(nlog2+qlog)
笔记
  • 这题的关键在于理清楚操作的本质,然后注意到偶数操作数的单调性以及奇数操作数潜藏的单调性,将问题转换为区间第 k k k 大问题。确实是很典型也很妙的主席树应用,值得记录。

【08J】Permutation and Primes

题意

构造一个长度为 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n105) 的排列,满足对于所有 1 ≤ i < n 1\le i<n 1i<n,要么 P i + P i + 1 P_i+P_{i+1} Pi+Pi+1 是奇质数,要么 ∣ P i − P i + 1 ∣ |P_i-P_{i+1}| PiPi+1 是奇质数。

做法
  • 方法很多,这里举一例。
  • [ 1 , n ] [1,n] [1,n] 8 8 8 个连续的数分一组,按照" a 1 , a 6 , a 3 , a 8 , a 5 , a 2 , a 7 , a 4 a_1,a_6,a_3,a_8,a_5,a_2,a_7,a_4 a1,a6,a3,a8,a5,a2,a7,a4" 的顺序循环放置。
  • 此时还剩下 n % 8 n\%8 n%8 个数,不妨令它们为 [ 1 , n % 8 ] [1,n\%8] [1,n%8],并放置在排列开头。对每一种情况进行特判,单独构造:
  1. [1]
  2. [1, 2]
  3. [1, 2, 3]
  4. [1, 4, 3, 2]
  5. [1, 4, 3, 2, 5]
  6. [1, 4, 3, 2, 5, 6]
  7. [1, 4, 7, 2, 3, 6, 5]
笔记
  • 这题比较容易想到是利用固定长度的循环来构造,并且用到的奇质数不会很多。但可以思考的方向太多了,一开始还是容易没有头绪。找到某一个较小整数(这题的 3/5/8 等)然后利用其性质进行构造的思路比较常见,值得学习。这里举一道由这题联想到的另一道构造题,也是利用某个较小整数的性质:https://ac.nowcoder.com/acm/contest/62977/F

【09G】Non-Puzzle: Game

题意

初始时黑板上写着 n ( n ≤ 1 0 6 ) n(n\le 10^6) n(n106) 个数 a i ( 0 ≤ a i < 2 30 ) a_i(0\le a_i<2^{30}) ai(0ai<230),给定一个数 k ( 0 ≤ k < 2 30 ) k(0\le k<2^{30}) k(0k<230),两个人轮流进行下述操作:

  • 在黑板上选择两次数(两次选择的可以是同一个数),记选中的数分别为 x , y x,y x,y,然后将 x ⊕ y x\oplus y xy 写到黑板上。

规定第一次写出 k k k 的人获胜;若进行无限回合都写不出 k k k 则平局。求先手必胜还是后手必胜还是平局。

做法
  • 分情况讨论:如果初始时黑板上就有两个数的异或为 k k k,则先手必胜;否则,如果无论先手怎么选择,黑板上都会有两个数的异或为 k k k,则后手必胜;如果上述两种情况都不成立,说明两人都不可能一步制胜,这意味着两人若进行有效操作改变局面,就有可能被对手抢占先机。所以此时两人都会摆烂,进行与上一次相同的无效操作,达成平局。
  • 对于第一种情况,只要用 set/unordered_set 判断 a i ⊕ k a_i\oplus k aik 是否存在即可。
  • 对于第二种情况,先手操作后得到 a i ⊕ a j a_i\oplus a_j aiaj,后手利用该数一定能获胜,相当于:对于所有 i , j i,j i,j,都存在 x x x 满足 a i ⊕ a j ⊕ a x = k a_i\oplus a_j\oplus a_x=k aiajax=k。等价变换后得:对于所有 i , j i,j i,j,都存在 x x x 满足 ( a i ⊕ k ) ⊕ ( a j ⊕ k ) = ( a x ⊕ k ) (a_i\oplus k)\oplus (a_j\oplus k)=(a_x\oplus k) (aik)(ajk)=(axk)。设 a i ′ = a i ⊕ k a_i'=a_i\oplus k ai=aik,则条件又转换为: a i ′ a_i' ai 构成的集合对于异或运算封闭。这意味着所有 a i ′ a_i' ai 张成的向量空间就是 a i ′ a_i' ai 构成的集合本身。因此只需要计算 a i ′ a_i' ai 的线性基维数 b b b,然后判断 a i ′ a_i' ai 构成的集合大小是否为 2 b 2^b 2b 即可(因为线性基本来就是由该集合算出来的,所以不需要一一检查集合中的数是否都在线性基确定的空间中)。
笔记
  • 这题的关键在于如何判断后手是否必胜。我一开始推出的条件是 { a i ⊕ a j ∣ i , j ∈ [ 1 , n ] } ⊆ { a i ⊕ k ∣ i ∈ [ 1 , n ] } \{a_i\oplus a_j|i,j\in[1,n]\}\subseteq \{a_i\oplus k|i\in[1,n]\} {aiaji,j[1,n]}{aiki[1,n]},然后错误地得到了 ∀ j ∈ [ 1 , n ] \forall j\in[1,n] j[1,n], a j = k a_j=k aj=k,贡献一发罚时。但实际上这里的两个 i i i 并不是同一个 i i i。做法里将 a i ⊕ a j ⊕ a x = k a_i\oplus a_j\oplus a_x=k aiajax=k 变化为 ( a i ⊕ k ) ⊕ ( a j ⊕ k ) = ( a x ⊕ k ) (a_i\oplus k)\oplus (a_j\oplus k)=(a_x\oplus k) (aik)(ajk)=(axk),然后转换视角到 a i ′ = a i ⊕ k a_i'=a_i\oplus k ai=aik 的操作很关键,也很妙,但是感觉不容易想到,可能还需要多练。

【09I】Non-Puzzle: Segment Pair

题意

给定 n ( n ≤ 5 × 1 0 5 ) n(n\le5\times 10^5) n(n5×105) 组区间,每组包含两个区间 [ l , r ] , [ l ′ , r ′ ] ( 1 ≤ l , r , l ′ , r ′ ≤ 5 × 1 0 5 ) [l,r],[l',r'](1\le l,r,l',r'\le 5\times 10^5) [l,r],[l,r](1l,r,l,r5×105)。现在要从每组中选择一个区间,满足得到的 n n n 个区间的交集不为空。求可行的选择方案数。

做法
  • 枚举交集的左端点 i i i,则相应的方案需要满足包含 i i i 而不包含 i − 1 i-1 i1。因此对于交集左端点为 i i i 的情况,可以先分别计算包含 i i i 的方案数和同时包含 i , i − 1 i,i-1 i,i1 的方案数,再相减得到合法的方案数。
  • 在进行上述过程之前,需要先进行预处理:对每组区间,用差分的方法对数轴上的段打统计标记,分别标记两个区间都包含的段和只有一个区间包含的段,最终统计出数轴上每个位置有多少组两区间都覆盖、有多少组只有一个区间覆盖。然后就可以开始根据这些信息计算每个左端点的方案数了。
笔记
  • 我在赛场上的做法本质上和题解一样,也是根据交集的左端点来划分所有方案,但利用的条件是“方案中至少有一个区间以 i i i 为左端点,且所有区间都包含 i i i”,并且不是枚举所有位置为左端点,而是直接枚举可能为左端点的位置(所有组中区间的左端点),搞复杂了。原因或许是没有想清楚交集以 i i i 为左端点的本质原因——不是“有区间以 i i i 为左端点”,而是“有区间不包含 i − 1 i-1 i1”。我的想法是“用一个以 i i i 为左端点的区间来保证 i i i 是交集左端点”,而不是“减去包含 i − 1 i-1 i1 的方案数来保证 i i i 是交集左端点”。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值