IOI 2020 集训队作业胡扯「1, 50」
IOI 2020 集训队作业胡扯「51, 100」(★)
IOI 2020 集训队作业胡扯「101, 150」
如果您点击了某个超链接,但是并没有发生任何变化,这意味着您可能需要在另外两章节中寻找对应内容。
表格
绿的表示主要没看题解,红的表示主要看了题解。
2020-01-15
agc020_e
等价于计算有多少个编码方式,解码后能得到 (S) 的子集。
假设长度为 (n) 的字符串 (s) 的答案为 (f(s)),则可以列出两种转移方式:
-
(f(s) =_+ f(s[1 : n - 1]) imes (1 + s[n]))。
表示:编码方式的最后一个字符为0
或1
,去掉这个字符后,前面的方案数即为 (f(s[1 : n - 1]))。
而当 (s[n] = 0) 时,最后的字符只能填0
,有 (1) 种方案;否则可以填0
或1
,有 (2) 种方案。 -
(f(s) =_+ f(s[1 : n - li]) imes f(igcap_{j = 1}^{i} s[n - lj + 1 : n - l(j - 1)]))((i ge 2),(l ge 1),(li le n))。
表示:编码方式的最后一个字符为)
,也就是说最后一段是(
(t)x
(i))
,枚举长度 (l = |t|) 和重复次数 (i)。
后面的 (t) 需要满足解码后是 (s) 中最后 (i) 段长度为 (l) 的子串的交的子集,也就是 (igcap_{j = 1}^{i} s[n - lj + 1 : n - l(j - 1)]) 的子集。
你可能会认为这样子时间复杂度太大,但是题解告诉我们,记忆化搜索后,有用的状态数是 (mathcal O (2^{|S| / 8} + {|S|}^3)) 的。每个状态可以花 (mathcal O (n^2)) 的时间进行转移,反正就是跑过了。
时间复杂度为 (mathcal O ({|S|}^2 2^{|S| / 8} + {|S|}^5)),评测链接。
cf566C
注意到 ({|x|}^{1.5}) 是下凸函数,也就是说每个位置都可以求出向最优解方向移动的倾向,也就是导数。
考虑点分树,从根开始利用导数的正负确定应该往哪个子树方向走,最多走 (log n) 层。
不需要显式建出点分树。
时间复杂度为 (mathcal O (n log n)),评测链接。
2020-01-16
cf587F
这题和 cf547E 极其相似,那题是问 (s_k) 在 (s_{l sim r}) 中的出现次数,这题是问 (s_{l sim r}) 在 (s_k) 中的出现次数。
令 (displaystyle sum_{i = 1}^{n} |s_i| = l),有 (l = Omega (n))。
先建出 AC 自动机,然后考虑一下答案如何表示。
令 (mathrm{id}_{i, j}) 为 (s_i[1 : j]) 在 AC 自动机上对应的节点。
特别地,(mathrm{id}_i = mathrm{id}_{i, |s_i|}) 为 (s_i) 在 AC 自动机上对应的节点。
则询问 ((l, r, k)) 的答案可以表示为:
令 fail 树中的 (mathrm{id}_{l sim r}) 的子树的权值都加上 (1)(重复的加多次)。
则答案为 (mathrm{id}_{i, 1 sim |s_i|}) 的权值和。
还以为和 cf547E 一样能有简单做法,结果想了半天都没想到 (mathrm{polylog}) 的做法,看了一下题解发现是根号的。
考虑根号分治,(|s_k| le T) 的和 (|s_k| > T) 的询问分开处理。
对于 (|s_k| > T) 的串,这样的串不会超过 (l / T) 个,考虑对于每个串使用线性的时间处理:
先把 (mathrm{id}_{k, 1 sim |s_k|}) 的权值定为 (1),然后在 fail 树上做一遍子树和,求出每个点子树中的权值和。
然后对于关于 (s_k) 的所有询问,拆分左右端点并按升序排序,用一个指针扫过所有询问端点,并用 (mathrm{id}_i) 的权值累加答案。
这一部分的时间复杂度为 (mathcal O (l^2 / T))。
对于 (|s_k| le T) 的串,每个询问考虑用 (mathcal O (|s_k|)) 的时间进行处理:
对于每个询问拆分左右端点并按升序排序,用一个指针扫过所有询问端点,每处理一个,就把 fail 树上 (mathrm{id}_i) 的子树的权值都加上 (1)。
对于每个询问 (s_k),查询 (mathrm{id}_{k, 1 sim |s_k|}) 当前的权值并累加,就是该询问的答案。
子树加,单点查使用 DFS 序 + 分块实现((B = sqrt{l})),这一部分的时间复杂度为 (mathcal O (n sqrt{l} + qT))。
令 (T = Theta (l / sqrt{q})) 可以取得最佳时间复杂度。
时间复杂度为 (mathcal O (n sqrt{l} + q + l sqrt{q})),评测链接。
2020-01-27
cf571E
记集合 ({a, a b, a b^2, a b^3, ldots }) 为 (langle a, b angle),特别地,记 ({a}) 为 (langle a, 1 angle)。
考虑合并 (langle a, b angle) 和 (langle c, d angle),形成新的集合,可以证明这个集合也可以表示成等比数列形式。
令所有 (n) 个 (a, b) 中涉及到的质数的集合为 (P),则可以把 (a, b, c, d) 表示为 (|P|) 维向量(第 (i) 个分量的大小为第 (i) 个质数在数值的质因数分解中的幂次)。
也就是要解 (vec{a} + k_1 vec{b} = vec{c} + k_2 vec{d}),其中 (k_1, k_2) 是非负整数。
考虑 (vec{b}) 和 (vec{d}) 是否为零向量,以及是否线性相关。
如果至少一个为零向量,可以直接判断,如果不线性相关,可以解二元一次方程组,如果线性相关,需要解线性同余方程。
大分类讨论即可。
时间复杂度为 (mathcal O (n^2 omega(v) log v)),其中 (v) 是值域,(omega(n)) 表示小于等于 (n) 的正整数的不同质因数个数的最大值,评测链接。
2020-01-28
cf590E
学习了一下偏序集上的最长反链求法。
令 (l) 为字符串长度之和。
先构造 AC 自动机,然后可以在 (mathcal O (l)) 的时间内求出每个串在每个结束位置包含的最长的小串是哪一个。
注意这并不是全部的包含关系,所以还要用 Floyd 求出传递闭包,用 bitset 优化。
然后是偏序集上求最长反链(求方案),参考 [CTSC2008]祭祀river 的做法。
代码中使用了 Dinic 求二分图匹配。
时间复杂度为 (displaystyle mathcal O !left( l + frac{n^3}{w} + n^{2.5} ight)),其中 (w) 为字长,评测链接。
2020-01-29
agc028_c
与其设置 (x o y) 这条边的权值为 (min(A_x, B_y)),我们看成有两条边,权值分别为 (A_x) 和 (B_y)。这样转化后,答案仍然相同。
我们考虑某种最终答案,假设走了 (x o y) 这条边,
如果走的是权值为 (A_x) 的边,我们把这条边染红色,
如果走的是权值为 (B_y) 的边,我们把这条边染蓝色。
也就是说,红色边的边权等于它的起点的 (A_x),蓝色边的边权等于它的终点的 (B_y)。
我们考虑最终的答案形成的环,要么全是红边,要么全是蓝边,要么红蓝交替。
这不是废话吗?为什么要分三类?
这是因为只有红蓝交替,才能够允许同时存在「入边和出边都是红色的点」和「入边和出边都是蓝色的点」。
也就是说,当且仅当红蓝交替时,才能任意选入边和出边的颜色,如果不是红蓝交替的,则颜色必须完全一样。
那么我们先让答案变成全选红色和全选蓝色的 (min),也就是 (sum A_i) 和 (sum b_i) 的 (min)。
然后考虑红蓝交替时的答案怎么计算:
因为红蓝交替时,必然会出现入边为蓝色,出边为红色的点。我们枚举这样的一个点 (i) 之后,把它的 (A_i) 和 (B_i) 都加进答案,只要在剩余的 (2n - 2) 个 (A_j) 和 (B_j) 中,选出最小的 (n - 2) 个,就一定能够组成合法的答案。
把标号和权值一起记下来排序,就能得到每个点的 (A_i) 和 (B_i) 的排名,分类讨论一下不难得到答案。
时间复杂度为 (mathcal O (n log n)),评测链接。
2020-01-30
arc097_f
我们的目标是把所有白点变黑。首先,特判掉没有白点,或者白点只有 (1) 个的情况。分别输出 (0) 和 (1)。
我们记 ({col}_i) 为点 (i) 的颜色,如果点 (i) 为白色,则 ({col}_i = 1),否则 ({col}_i = 0)。
剩下的情况中,我们把包含所有白点的极小连通块抽离出来(可以通过不断删除黑色叶子实现)。
显然,这个连通块形成了有 (ge 2) 个点的一棵树。令连通块中的点数为 (num)。
不难发现,最优解中,猫一定不会踏出这个连通块。
但是又必须经过连通块内的每个点(因为必须经过连通块里的所有叶子,连通块中的所有叶子都是白点)。
那么接下来,我们只考虑这个连通块,定义 ({deg}_i) 为连通块中点 (i) 的度数(不考虑原树中的边)。
又有结论:猫不会经过一条边 (ge 3) 次,也就是说只会经过 (1) 次或 (2) 次。
(这是因为多余的操作都可以简化成“改变当前点的颜色”的操作,一定不会更劣)
我们先假设:猫必须回到起点。
可以发现这时猫的起点对答案已经没有影响了。因为每条边都会被正向反向分别经过恰好 (1) 次。
也就是说,先规划好了路线,计算这个路线对点颜色的影响。在这之后再“插入”改变当前点颜色的操作。
那么显然,在这个情况下,如果 ({deg}_i) 为奇数,则 ({col}_i) 就会因为该点被到达了奇数次而发生改变,否则 ({col}_i) 保持不变。
最终,答案就等于 (2 cdot (num - 1) + sum {col}_i),也就是 (2) 乘边数,加上 ({col}_i = 1) 的点数,这些点需要额外使用一次操作。
问题在于:猫不一定要回到起点。
假设猫从 (x) 出发,最终到达了 (y)。令 (x) 与 (y) 之间的边数为 (len)。
则可以少走 (len) 条边,也就是不用从 (y) 再返回 (x) 了。
但是少走的这 (len) 条边,对 (col) 也有影响:
因为从 (y) 返回 (x) 的边少走了,所以 (x) 到 (y) 路径上的点(除了 (y) 本身)的 (col) 都会发生变化。
实际上恰好有 (len) 个点的 (col) 发生了变化。
如果 (col) 从 (1) 变成了 (0),可以少花一次操作。但是从 (0) 变成 (1) 的话,就要多花一次操作。
也就是说,平均算下来,如果原先的 (col) 是 (1),可以少花两次操作;如果是 (0),则不亏不赚。
总的来说,就是这条路径,经过的 ({col}_i = 1) 的点要尽量多(但是不包含终点)。
一个观察:叶子的 (col) 始终为 (0)(因为原先叶子的 (col = 1),但是叶子的 (deg) 也是 (1),所以抵消了)。
而这条路径自然是越长越好,肯定会延伸到叶子,所以包不包含终点也没有影响了。
树形 DP 计算经过的 ({col}_i = 1) 的点最多的路径就行了。
假设最多经过 (ans) 个 ({col}_i = 1) 的点,则最终答案为 (2 cdot (num - 1) + sum {col}_i - 2 cdot ans)。
时间复杂度为 (mathcal O (n)),评测链接。
agc030_d
因为 (N) 很小,考虑直接枚举位置对 (langle i, j angle)((1 le i < j le N))以统计逆序对。
考虑一个概率 DP:令 (f(i, j))((1 le i, j le N))为当前时刻下,(A_i > A_j) 的概率。
(这里假设对于 (Q) 个操作,每个操作都以 (1 / 2) 的概率执行)
那么最终时刻下,满足 (i < j) 的 (f(i, j)) 之和,再乘以 (2^Q) 就是答案(期望的线性性)。
按顺序考虑每个时刻(操作),考虑新的 (f(i, j)) 和原先的比有什么变化。
可以发现只有 (mathcal O (N)) 个位置会发生变化。具体地说,只有 (i, j) 有至少一个等于 (X_i) 或 (Y_i) 时才有可能发生变化。
暴力转移即可。
时间复杂度为 (mathcal O (N (N + Q))),评测链接。
arc093_e
考虑原图的一棵生成树 (T),边权和为 (S)。
对于一条非树边 (e),定义 (v(e)) 为 (W_e) 减去「(U_e, V_e) 在 (T) 上的路径上的最大边权」。
也就是说,(v(e)) 表示用 (e) 替换 (T) 中的一条边,形成新的生成树的最小代价。显然有 (v(e) ge 0)。
假设染色方案已确定,如何计算满足条件的最小生成树?
- 如果 (T) 中的边不同色,则 (T) 就是合法的最小生成树,权值为 (S)。
- 否则 (T) 中的边都是同一种颜色,取 (v(e)) 最小的异色的 (e),则合法的最小生成树的权值为 (S + v(e))。
- 如果不存在这样的 (e)(所有边全部同色),则不存在合法的生成树。
那么答案就很显然了,先把 (X) 减去 (S)。
如果 (X < 0),则无合法解,输出 0
。
如果 (X > 0),则 (T) 必须同色,且 (v(e) < X) 的边都必须染相同颜色,(v(e) = X) 的边必须至少有一个染不同颜色。
如果 (X = 0),则是上面的答案的基础上,再加上 (T) 不同色,其它边任意选的方案数。
容斥一下答案就出来了。
时间复杂度为 (mathcal O (M log M)),评测链接。
2020-02-01
agc024_d
观察样例,再自己手造几组数据,就可以发现,最终长成的树必然是:
- 以一个点为中心的放射状。
- 以一条边为中心的放射状(两侧对称)。
更具体地说,也就是把中心点或中心边「拎起来」,下面挂着的树形态,满足每个深度相同的子树都完全一致。
而这时,答案就为树的深度(假设中心点的深度为 (1),中心边的两端点深度为 (1))。
而叶子个数,就等于每一个深度的节点的,子节点个数的乘积(包括深度 (1),但不包括叶子所在的深度)。
那么我们枚举中心点,或者中心边,把原树「拎起来」,原树就变成了一个奇怪的形状。
那么显然我们需要补最少的点,对于每个深度,把子节点个数取 (max) 再相乘,就得到最终的最少叶子个数了。
每个点和边都枚举一遍,取最优解。
时间复杂度为 (mathcal O (n^2)),评测链接。
arc091_f
这题真的好简单……
显然我们求一下每堆石子的 SG 函数就好了。
我们打表找规律,当 (K = 1) 的时候,就是普通的 Nim。如果 (K ge 2) 会怎么样?
以 (K = 4) 为例:
不难发现规律:
然而直接这样算有点慢,不难发现,如果按照 (displaystyle leftlfloor frac{n}{k} ight floor) 分段,则每一段内是循环的。
可以把第三行改写成:(displaystyle operatorname{SG}_k !left( n - !leftlfloor frac{displaystyle n - !leftlfloor frac{n}{k} ight floor! cdot (k - 1)}{displaystyle leftlfloor frac{n}{k} ight floor! + 1} ight floor! cdot !left( leftlfloor frac{n}{k} ight floor! + 1 ight) ight))。
这样子计算,可以证明会在 (sqrt{n}) 次内结束。
时间复杂度为 (mathcal O (N sqrt{A})),评测链接。
2020-02-02
agc035_c
当 (N) 是 (2) 的次幂时无解,这是因为 (N) 的最高位无法被异或得到。
否则一定有解,构造如下(令 (m) 为小于等于 (N) 的最大的 (2) 的次幂,(k) 为 (N - m)):
当 (N) 是偶数时才需要连 ((m+1) leftrightarrow N) 和 (k leftrightarrow N')。
时间复杂度为 (mathcal O (N)),评测链接。
2020-02-03
cf603E
考虑给定了边集后,如何判断是否存在合法的选边方案。
容易证明存在一种方案,当且仅当每个连通块的大小都是偶数。
(考虑每个连通块仅保留一棵生成树,再在生成树上自底向上选边)
所以先把 (n mod 2
e 0) 的情况判掉(全部输出 -1
)。
那么相当于每次我们可以尝试不断把权值最大的边删去,使得删去后图的每个连通块大小仍为偶数。
加边删边时,使用 LCT 维护最小生成树即可。
判断一条边是否需要删除,需要用能够统计虚子树信息的 LCT,这里虚子树信息就是子树大小。
为了方便考虑,代码中在初始的时候就加上了 (n / 2) 条边,第 (i) 条连接 (2i - 1) 和 (2i),权值为 (infty)。
这样做可以保证在一开始的时候每个连通块大小都是偶数,省去了不必要的特判,如果算出来答案为 (infty),就输出 -1
。
时间复杂度为 (mathcal O (m log m)),评测链接。
2020-02-04
agc034_e
既然所有棋子最终到达了同一个点,我们先考虑枚举这个点,记做 (r),以 (r) 为根建树。
记 (s_i) 表示 (i) 子树中的所有点 (A_i) 之和,也就是子树中的棋子个数;
记 (w_i) 表示 (i) 子树中的所有棋子到 (i) 的距离之和。
这样,(s_i) 和 (w_i) 都可以很容易地计算。
(具体地说,(displaystyle w_u = sum_{v in mathrm{sons}_u} w_v + s_v))
考虑做题目中的操作会带来的影响,假设操作的棋子分别位于 (x, y):
- (x, y) 为祖孙关系。这个操作对 (r) 本身,以及 (r) 的任意一个子树方向的 (w) 值都不会产生影响(注意不是子树,而是这个子树对 (w_r) 产生的贡献)。
- (x, y) 在 (r) 的不同子树中。这个操作会使 (r) 的那两个子树方向的 (w) 值各减去 (1)。
- (x, y) 在 (r) 的同一个子树中,但是不为祖孙关系。这个操作会使 (r) 的那一个子树方向的 (w) 值减去 (2)。
可以发现:如果 (w_r) 为偶数,在所有子树方向的贡献中,最大的那一个,也不超过 (w_r) 的一半的话,就可以不断通过 2 操作把某两个子树方向的贡献减去 (1),最终使得 (w_r) 变成 (0)。也就意味着所有棋子都到达了 (r)。(具体方法是,每次都选择当前贡献最大的子树中的一个棋子进行操作)
但是如果贡献最大的那个子树,它的贡献超过了 (w_r) 的一半怎么办呢?这时就需要在这个子树中使用 3 操作以减小这个子树的贡献。可以发现这个贡献减得越小越好。
这启发我们定义 (f_i) 表示只操作 (i) 子树中的棋子,能够把 (w_i) 最小减少到多少。就是做操作后 (w_i) 的最小值。
这样一来,只要 (f_r = 0),就意味着可以把棋子都聚集到 (r) 上,且根据上面的过程,恰好花费 (w_r / 2) 步。
(因为没必要使用 1 操作,而 2, 3 操作都会让 (w_r) 减去 (2))
我们考虑递归地计算 (f_i)。要求 (f_u) 时,考虑 (u) 的每个子节点 (v) 的 (w_v) 和 (f_v)。
同样地,考虑对 (w_u) 贡献最大的那个子树 (v),我们使用 3 操作,让它的 (w_v) 变成了 (f_v)。
这样一来,若 ((f_v + s_v) le w_u / 2),则 (f_u = w_u mod 2),否则 (f_u = (f_v + s_v) - (w_u - (w_v + s_v)))。
这样就可以在 (mathcal O (N)) 的时间内计算最终聚集到点 (r) 的答案了。总时间复杂度为 (mathcal O (N^2)),足以通过该题。
显然这个式子是可以换根 DP 的,所以代码中也就写了,时间复杂度变为 (mathcal O (N))。
时间复杂度为 (mathcal O (N)),评测链接。
agc026_d
去年 CSP 前,做过一个比赛,出题人搬了这题,我打了一整场比赛把它切掉了,其他题没分(
PS:数据范围改成了 (1 le N le {10}^5)。
注意:我的做法与题解的做法有较大出入。
我们首先考虑这样的 DP:令 (dp(i)) 表示前 (i) 列已经染色,其他列还未染色的方案数。
但是这样定义的状态是没法转移的。
为什么?我们先考虑一下转移的方式:
- 如果相邻的两个格子为同种颜色(
##
或##
或竖着排列的),则它们所在的 (2 imes 2) 区域中的颜色将被唯一确定。
即##
或
####
或它们旋转 ({90}^{circ}) 的结果。
## - 如果相邻的两个格子的颜色不同(
##
或##
或竖着排列的),在不考虑其它限制的情况下,它们所在的 (2 imes 2) 区域中的颜色是有两种方案的。
即##
或其旋转 ({90}^{circ}) 的结果。
##
现在,考虑前 (i - 1) 列已经染色,第 (i) 列以及之后还未染色的情况。
那么第 (i) 列的染色情况,可以只由第 (i - 1) 列的染色情况确定。
下面举几个例子(假设第一列为第 (i - 1) 列,第二列为第 (i) 列):
?
,这个情况下第 (i) 列有 (8) 种染色方案:最底下 (6) 行 (2) 种,上面两个格子 (2 imes 2 = 4) 种。
?
#?
#?
#?
#?
#?
#?
?
,这个情况下第 (i) 列有 (4) 种染色方案:最底下 (6) 行 (1) 种,上面两个格子 (2 imes 2 = 4) 种。
?
#?
#?
#?
#?
#?
#?
#
,这个情况下第 (i) 列有 (2) 种染色方案。
#
#?
#?
#?
#?
#?
#?
#
,这个情况下第 (i) 列有 (1) 种染色方案。
#
#?
#?
#?
#?
#?
#?
可以发现,关键就在于第 (i - 1) 列的,最靠下的,上下相邻两个格子颜色相同的位置。
如果这个位置在第 (i) 列的范围内,则第 (i) 列的最底下 (min(h_{i - 1}, h_i)) 个格子的颜色就会被唯一确定,否则恰有两种方案。
剩下 (max(h_i - h_{i - 1}, 0)) 个格子可以任意染色。
可能可以考虑记状态 (dp(i, j)) 表示前 (i) 列已经染色,且第 (i) 列最靠下的上下相邻两个格子颜色相同的位置为 (j)。
但是这样状态也太多了,也没法做。
最后我们考虑记状态 (dp(i)) 表示:前 (i) 列已经染色,且保证第 (i) 列任意两个相邻格子的颜色都不同的方案数。
也就是第 (i) 列颜色红蓝交错的方案数。这样令 (h_{n + 1} = 1),则 (dp(n + 1) / 2) 就是答案。
我们考虑转移,如果 (h_{i - 1} le h_i),也就是第 (i) 列变高了,则 (dp(i) = 2 cdot dp(i - 1))。
这是因为我们限制了第 (i) 列是红蓝交错的,又因为 (h_{i - 1} le h_i),所以第 (i - 1) 列也必然是红蓝交错的,可以直接转移。
但是如果 (h_{i - 1} > h_i) 怎么办?这时限制第 (i) 列红蓝交错,不能保证第 (i - 1) 列也是红蓝交错的。
先小结一下:可以发现,如果第 (x) 列的最底下 (w) 个格子是红蓝交错的,那么对于 (y = x - 1) 或 (x + 1),也就是 (x) 的相邻列,可以推出第 (y) 列的最底下 (min(w, h_y)) 个格子是红蓝交错的。
这引出一个结论:如果第 (x) 列是红蓝交错的,则第 (y) 列是红蓝交错的充分条件是:对于 (x le i le y) 有 (h_i ge h_y)。
换句话说,就是 (h_y) 是 (x, y) 之间(包含 (x, y))的 (h) 的最小值。
回到 DP 上,仍然是要转移 (dp(i)),我们假设前一个一整列都是红蓝交错的列为第 (j) 列。
则这个 (j) 要满足 (displaystyle h_j < min_{j < k < i} h_k),且「(h_j ge h_i) 或『(h_j < h_i) 且 (displaystyle min_{j < k < i} > h_i)』」。
这等价于 (j) 是前 (i - 1) 列单调栈中的元素(我们维护 (h) 值严格递增的单调栈),且当加入 (i) 时 (j) 被弹栈或 (j) 为弹栈后的栈顶(但不能弹出 (h) 值恰好等于 (h_i) 的元素)。
根据单调栈的结论,总转移数是 (mathcal O (N)) 的,这是可以接受的。接下来要考虑转移的系数。
我们首先考虑第一类转移,也就是 (j) 为被弹栈的元素((h_j ge h_i))的转移:
我们考虑上图,(j = 5),(i = 11)。
(j) 到 (i) 之间的 (h) 值依次为 (6, 10, 11, 8, 12, 10, 3)。
我们已知第 (j) 列是红蓝交错的,而之后的每一列都必须不是红蓝交错的。
如果出现了类似图二中的情况,也就是在“中间部分”(不包括 (j) 和 (i) 的部分)出现了相邻两列颜色没有翻转的话,就会导致“中间部分”出现一整列都是红蓝交错的情况,不符合要求。
也就是说“中间部分”必须满足:每一列的颜色都和上一列的不同,除了最初的那一列(第 (j + 1) 列)。
又因为第 (i) 列的颜色也可以选择翻转或不翻转,所以这里总共贡献了 (2^2 = 4) 的系数。
考虑图三和图四的上面的部分,因为不能让第 (8) 列出现一整列都是红蓝交错的情况,需要存在一行的颜色不翻转,这里有 (2) 种情况。
而更上面未染色的部分,每一段红色的线条都会产生 (2) 的贡献(可以选择翻转或不翻转)。
为什么有恰好 (2) 种情况呢?这是因为 (h_8 - h_j = 8 - 6 = 2)。
这一部分的总贡献为 (2^7 + 2^8),实际上是一个等比数列的形式。
这个等比数列的首项为 (2^{mathrm{num1}}),末项为 (2^{mathrm{num2} - 1})。总和就为 (2^{mathrm{num2}} - 2^{mathrm{num1}})。
这里就有 (mathrm{num2} = 9),以及 (mathrm{num1} = 7)。
而 (mathrm{num1}, mathrm{num2}) 如何计算呢?
我们记 (b_i) 表示前 (i) 列的形如这样的红线个数。可以发现 (mathrm{num2} = b_{i - 1} - b_j),而 (mathrm{num1} = mathrm{num2} - (h_k - h_j))。
其中 (k) 就为上一个被弹栈的元素(这里 (k = 8)),也就是满足 (j < k) 的 (h_k) 最小的元素之一。
综上所述,(j) 对 (i) 产生的贡献就为 (4 (2^{mathrm{num2}} - 2^{mathrm{num1}})) 倍的 (dp(j))。
(特殊情况是 (j = i - 1),因为“中间部分”完全消失了,所以不能套用上面的推导,这时贡献为 (2))
然后我们考虑第二类转移,也就是 (j) 为弹栈后的栈顶,且上一个被弹栈的元素的 (h) 值不等于 (h_i):
仍然是 (i = 11) 的情况,此时 (j = 2)。
如图二,同样地我们不能允许出现在“中间部分”的相邻两列颜色不翻转的情况。
特别地,如图三,我们需要的红蓝交错的高度不是 (h_j) 而是 (h_i),因为 (h_i > h_j)。
如图四,可以发现此时颜色不翻转的行,只有 (1) 种取值了,也就是 (h_3 - h_i = 4 - 3 = 1)。
而上面的部分仍然和前一种情况类似,我们可以求出此时的 (mathrm{num2} = b_{i - 1} - b_j - (h_i - h_j)),而 (mathrm{num1} = mathrm{num2} - (h_k - h_i))。
其中 (k) 的含义和前文相同,为上一个被弹栈的元素。
综上所述,(j) 对 (i) 产生的贡献就为 (4 (2^{mathrm{num2}} - 2^{mathrm{num1}})) 倍的 (dp(j))。
(特殊情况是 (j = i - 1),因为“中间部分”完全消失了,所以不能套用上面的推导,这时贡献为 (2))
然而其实我们漏掉了一种情况,要是这样的 (j) 不存在呢?也就是不存在红蓝交错的前一列该怎么办?
其实这无关紧要,我们只要让 (h_0 = 1),然后设 (dp(0) = 1) 就行了,不影响后续计算。
时间复杂度为 (mathcal O (N log mathrm{MOD})),其中 (log mathrm{MOD}) 为快速幂复杂度,评测链接。
2020-02-05
arc103_d
令值域为 (v),本题中 (v = {10}^9)。
每次操作,你必须让 (x) 坐标或 (y) 坐标增加或减少一个定值 (d)。
令一个点 ((x, y)) 的奇偶性为 ((x + y) mod 2),可以发现能够操作到的点的奇偶性都相同,所以先把无解判掉。
好像没有什么特别的构造方法。我们先考虑二进制拆分。
但是搞了半天之后,发现二进制拆分好像只能做到 (2 (lfloor log_2 v floor + 1)) 次操作。
然后我们惊奇地发现,(2 (lfloor log_3 v floor + 2)) 好像恰好就是 (40) 啊。
所以我们考虑把坐标表示成三进制数,注意这里三进制的系数,应该是 (0, 1, -1) 三种。
那么我们令 (m = 40),(d_1 sim d_m) 分别为 (1, 1, 3, 3, 9, 9, 27, 27, 81, 81, ldots)。
也就是对于每一个 (0 le i le 19),(3^i) 在 (d) 中出现两次。
(如果所有点的奇偶性为 (1),则去掉一个 (3^{19}))
接下来构造解,每一位使用恰好两个 (3^i):
我们从低位到高位考虑,假设考虑到了第 (k) 位。记 (x_k, y_k) 分别表示 (x, y) 坐标第 (k) 位的值。
- (x_k = 0, y_k = 0):
做RL
操作,也就是让 (x) 减去 (1) 再加上 (1),也就是不变。 - (x_k
e 0, y_k
e 0):
做R/L
操作中的一个,让 (x_k) 变成 (0);
做U/D
操作中的一个,让 (y_k) 变成 (0)。 - (x_k
e 0, y_k = 0):
如果 (x_k = 1),做LL
操作,让 (x_k) 变成 (3);
如果 (x_k = -1),做RR
操作,让 (x_k) 变成 (-3);
无论是哪种操作都需要进/借位,最后 (x_k) 还是变成了 (0)。 - (x_k = 0, y_k
e 0):
类似上一种情况。
特别地,如果考虑到了第 (k = 19) 位:
- 点的奇偶性为 (0):只会出现上述情况中的 1, 2 两种。
- 点的奇偶性为 (1):只剩一个 (3^{19}) 了,(x_k, y_k) 也必然只有一个非 (0),做
R/L/U/D
操作中的一个即可。
因为 (lfloor log_3 v floor = 18),所以可以证明在执行上述操作时,只会影响到第 (0 sim 19) 位。
时间复杂度为 (mathcal O (N log_3 v)),评测链接。
看了题解后发现我是睿智,可以用二进制的,而且只需要 (32) 次。
2020-02-06
cf553E
令 (f(i, k)) 为第 (i) 个点在 (k) 时刻,最佳策略下到达终点的代价期望值;类似定义 (g(j, k)) 表示第 (j) 条边的代价期望值。
则 (f) 和 (g) 互相转移,以 (k) 为阶段可以发现都是从大往小转移。最终答案就是 (f(1, 0))。
固定 (f(i, ast)) 和 (g(j, ast)),转移形式是一个卷积,用分治 FFT 优化即可。
一开始的边界条件要先跑一下最短路。
时间复杂度为 (mathcal O (n^3 + m t log^2 t)),评测链接。
agc022_e
这道题挺神的,我尝试用比较好理解的方式解释一下:
我们考虑判断一个已知的串是否合法。
首先一个显然的事实:假设我们把串的某一位从 0 变成了 1,那么一定是变优了,反之一定是变劣了。
也就是说,单独修改某一位的优劣性,是可以简单判断的。
接下来我们列举一些操作的情况:
11a => 1
10a => a
01a => a
00a => 0
1ab => (a|b)
0ab => (a&b)
又有一结论:如果 (S) 形如 11...,那么一定合法,这是因为,只要把后面的部分缩起来,再缩 11a 得到 1 即可。
我们再考虑串形如 00... 时,因为迟早要对 00 进行操作,我们不妨直接考虑对 00 进行操作时:
假设串形如 00ab...,那么操作 00a 得到 0b...,操作 0ab 得到 0(a&b)...。
显然前者不劣于后者,所以直接操作前者即可。也就是说 00a... == 0...。
考虑串形如 01... 时,同样地,直接考虑对 01 进行操作时:
假设串形如 01ab...,那么操作 01a 得到 ab...,操作 1ab 得到 0(a|b)...。
当 a=0 或 b=1 时,前者不劣于后者。当 a=1 且 b=0 时,两种操作分别得到 10... 和 01...。
到底是 10... 优还是 01... 优呢?这里我们先不作解答。
考虑串形如 10... 时:
假设串形如 10ab...,那么操作 10a 得到 ab...,操作 0ab 得到 1(a&b)...。
当 a=1 时,前者不劣于后者。当 a=0 且 b=0 时,后者不劣于前者。当 a=0 且 b=1 时,两种操作分别得到 01... 和 10...。
又回到了比较 10... 和 01... 的问题上。
我们考虑 10xy... 和 01xy...:
- 如果 x=0,则前者可以得到 0y... 或 10...;而后者可以得到 0y...。
此时,前者不劣于后者。 - 如果 x=1,则前者可以得到 1y...;而后者可以得到 1y... 或 01...。
如果 y=1,前者不劣于后者。
如果 y=0,两者都能得到 10...,而后者还可以得到 01...。
但是不能无限地往后接 10 循环,最终一定会变成 10a 或 01a,但是它们的结果确实是相同的,没有孰优孰劣。
所以结论是 10... 比 01... 更优。
一个例子是:10001 和 01001,前者是合法的串,而后者不是。
那么我们构造如下转移图:
其中绿边表示 (0) 的转移,红边表示 (1) 的转移,橙边表示 (0, 1) 都是这个转移。
然后在这个自动机上转移即可。最终答案为 1 和 11 上的方案数之和。
时间复杂度为 (mathcal O (|S|)),评测链接。
arc101_e
直接 DP 的话大概是 (mathcal O (N^3)) 的,使用容斥原理:
要计算的就是 (displaystyle sum_{S subseteq E} {(-1)}^{|S|} f(S)),其中 (f(S)) 表示强制不能覆盖到 (S) 中的边的方案数。
也就是把 (S) 内的边都删掉,原树被分割成了若干连通块,每个连通块内部连线的方案数。
显然一个大小为 (n) 的连通块,任意连线的方案数为 (1 cdot 3 cdot 5 cdot cdots cdot (n - 1))(如果 (n) 是偶数,(n) 是奇数的话显然没有方案)。
那么设状态 (dp(i, j)) 表示考虑 (i) 的子树,割掉若干条边,(i) 所在的连通块大小为 (j),的贡献总和(不算 (i) 所在连通块的贡献)。
转移也是显然的。
时间复杂度为 (mathcal O (N^2)),评测链接。
arc101_f
首先,每个机器人只会从它旁边的两个出口离开。
对于旁边只有一个出口的机器人,直接不管它就行,不会影响答案。
对于旁边有两个出口的机器人,我们只需要保存它到两个出口之间的距离。
上述处理都可以通过双指针来完成。
现在问题就变成了,给定 (k)((0 le k le N))个机器人,第 (i) 机器人到它左边和右边的出口的距离分别为 (x_i) 和 (y_i)。
我们把每个机器人画在坐标系中,第 (i) 个机器人的位置就是 ((x_i, y_i))。
可以发现,相当于我们操控两条直线,初始时这两条直线的位置分别为 (x = 0) 和 (y = 0),也就是在坐标轴上。
我们可以把竖线往右移动,把横线往上移动。如果线碰到了一个点,这个点就被这条线吃掉,然后消失。
问点被线吃掉的,不同的方案数。
我们考虑这两条线的交点形成的轨迹,可以发现,在轨迹上方的点是被竖线吃掉的,在轨迹下方的点是被横线吃掉的。
那么我们考虑统计这条轨迹穿过这些点的方案数。限制是轨迹必须是增函数。
我们把坐标离散化一下,用树状数组优化 DP 就可以做了。
具体地说,我们把所有点按照横坐标递增排序。然后考虑某条竖线上的点如何转移。
令 (dp(i, j)) 表示,考虑了横坐标 (le i) 的点了,并且轨迹下的点的纵坐标最大值为 (j) 的方案数。
则 (dp(i, j)) 可以转移到 (dp(i + 1, j)) 和 (dp(i + 1, y))(存在点 ((i + 1, y)) 满足 (y > j))。
用树状数组维护。
时间复杂度为 (mathcal O (M + N log N)),评测链接。
2020-02-07
arc103_f
假设满足条件的树为 (T),我们考虑 (T) 的一些性质。
首先 (T) 只有一个重心,因为如果有两个,那么它们两个的 (D) 值就应该相同,与题意矛盾。
显然重心就是 (D) 值最小的那个点。
令 (T) 以重心为根,容易发现,任何一个非根节点的 (D) 值都大于它的双亲节点的 (D) 值。
我们选取 (D) 最大的那个点 (u),显然它必须是叶子。
我们可以计算出它的双亲节点的 (D) 值应该恰好为 (D_u - N + 2 cdot mathrm{siz}),其中 (mathrm{siz}) 为 (u) 的子树大小(此时为 (1))。
那么就可以唯一确定它的双亲节点是哪一个。
然后把该点“删去”,并让它的双亲节点的 (mathrm{siz}) 加上 (1)。
重复 (n - 1) 次这个过程。就可以确定出 (T) 的形态。
也就是说,满足条件的 (T) 其实是唯一的。
这时我们再 DFS 检查一下是否确实满足条件即可。
时间复杂度为 (mathcal O (N log N)),评测链接。
agc020_d
首先我们可以得出:最短的连续段长度等于 (displaystyle leftlceil frac{max{A, B}}{min{A, B} + 1} ight ceil! = !leftlfloor frac{A + B}{min{A, B} + 1} ight floor),令这个值为 (K)。
观察 (A ge B) 的情况(留作练习,请读者自行完成)(大雾)
可以发现,一组 (A, B) 的答案,就是当 (A = (B + 1) K) 时的答案(形如 ([B cdot (K cdot exttt{"A"} + exttt{"B"}) + K cdot exttt{"A"}])),
去掉靠后的恰好 ((B + 1) K - A) 个 ( exttt{"A"}) 得到的,令这个值为 (mathrm{del})。
但是并不一定是去掉最后 (mathrm{del}) 个 ( exttt{"A"}),考虑 (A = 8, B = 6) 的情况:( exttt{"AABAABAABABABB"})。
它就是 (A = 14, B = 6) 的情况:( exttt{"AABAABAABAABAABAABAA"}) 去掉 (6) 个 ( exttt{"A"}) 得到的,但是不是最后 (6) 个。
这是因为如果去掉了最后 (6) 个,就会有 (3) 个 ( exttt{"B"}) 形成连续段,与「连续段长度不大于 (2)」冲突。
也就是说,贪心地去掉尽可能靠后的 (mathrm{del}) 个 ( exttt{"A"}),但是不形成 ((K + 1) cdot exttt{"B"}) 的连续段。
讨论一下就可以得到结果了。
对于 (A < B) 的情况类似,是 (B = (A + 1) K) 时的答案(形如 ([A cdot (K cdot exttt{"B"} + exttt{"A"}) + K cdot exttt{"B"}])),
去掉尽可能靠前(不是靠后)的若干个 ( exttt{"B"}) 得到的,类似做就行了。
时间复杂度为 (mathcal O (Q (D - C + 1))),评测链接。
2020-02-08
arc099_f
考虑记前缀和信息 ((Lambda_i, delta_i)) 表示依次吃掉 (1 sim i) 的所有字符后,得到的序列和指针 (P) 的位置。
那么两信息的合并:((Lambda_1, delta_1) circ (Lambda_2, delta_2) = (Lambda_1 + Lambda_2 gg delta_1, delta_1 + delta_2))。
我们记 (h_i) 为 (displaystyle sum_{p} Lambda_i[p] eta^p)。
则有 ((h_1, delta_1) circ (h_2, delta_2) = (h_1 + h_2 eta^{delta_1}, delta_1 + delta_2))。
考虑 (g = h_n),一个区间 ([i, j]) 的信息等于 (g) 当且仅当 (h_{j - 1} + g eta^{delta_{j - 1}} = h_i)。
那么我们从后往前枚举 (j),维护一个 (h_i) 的桶,以统计答案。
把 (h) 哈希一下就可以存进桶里了,为了防止冲突需要使用双哈希。
时间复杂度为 (mathcal O (N cdot mathcal T (mathrm{hash}))),评测链接。
agc031_d
根据题意,我们可以得到 (a_i = a_{i - 1} {(a_{i - 2})}^{-1})。其中 (q p) 表示置换的复合,满足 ({(q p)}_i = q_{p_i})。
将 (a_0) 至 (a_{11}) 列出,可得:
所以是有个长度为 (6) 的循环节。
令 (z = q p^{-1} q^{-1} p),则 (a_K) 可以表示为 (z^{K div 6} a_{K mod 6} {(z^{-1})}^{K div 6})。
时间复杂度为 (mathcal O (N log K)),评测链接。
cf587D
容易发现这是一个 2-SAT 的形式。
要求删除 (t) 尽量小的边,那就先二分答案。
不过还是要考虑建图的问题。
如果直接建图的话,会发现每个点周围的边都会互相影响,也就是删掉其中一条就不能删另一条。
直接建边的话要建 (mathcal O ({deg}^2)) 条。
其实限制就是,一个点周围的边,最多删一条。
那么我们再在 2-SAT 模型中新建若干个变量,(s_i) 表示前 (i) 条边是否被删去。
如果 (s_i = 1),那么就有 (s_{i + 1} = 1),(a_{i + 1} = 0)。
如果 (a_i = 1),那么就有 (s_i = 1)。
大概是个链前缀和的形式。
这样子边数就是线性的了。
时间复杂度为 (mathcal O (m log m + m log t)),评测链接。
cf674F
这里我就抄 CMXRYNP 的题解了。
从能够得到的信息考虑。
最多有 (min{p, n - 1}) 只熊要去睡觉,我们先让 (p) 对 (n - 1) 取 (min)。
那么对于每只熊,我们可以知道它有没有去睡觉,如果有可以知道它在第几晚去睡觉的。
所以最多的情况数就是:
也就是枚举 (k) 只熊去睡觉了,然后 (i) 是天数。
这是桶数量的一个上界。因为再怎么改变策略最后也只有这么多种情况。
我们可以证明这个上界是可以被达到的。
把所有情况编号,对于第 (x) 种情况,令没有去睡觉的熊不碰第 (x) 个桶,在第 (a) 天去睡觉的熊恰好在第 (a) 晚去喝第 (x) 个桶里的饮料。
这样子如果第 (x) 桶里是酒,就恰好会生成这种情况,所以每种答案对每种情况是一个满射,然而它们数量相同,所以是一一对应的。
所以只要求出这个多项式的系数就行,系数就是组合数模 (2^{32}),记一下 (2) 的次数,递推一下就行。
时间复杂度为 (mathcal O (p log mathrm{MOD} + q cdot p)),其中 (log mathrm{MOD}) 为快速幂复杂度,评测链接。
agc024_e
相当于是,从一个空串开始,每次往里面加入一个 ([1, K]) 之间的字符,加入 (N) 次,使得字符串的字典序递增。
先把每个串的末尾加上字符 (0)。
可以发现,加入字符(假设我们在字符 (y) 前插入字符 (x))后字典序递增,当且仅当:
- (x > y)。
- (x = y),且 (x > y) 之后第一个不同的字符。
我们插入的时候,只关心字符串是否相同,不关心插入的位置。
所以假设是第二种情况,我们可以把插入的位置往后挪到那个不同的字符之前。
这样子既不会统计到重复的串,又避免了难考虑的 2 情况,一举两得。
现在只需要考虑 1 情况,也就是每次插入的字符必须插入在一个比它小的字符之前。
我们可以把插入的形式抽象化为一棵树:
根节点为 (0),权值也为 (0)。
假设第 (i) 次在字符 (y) 之前插入了字符 (x),如果 (y) 是在第 (j) 次插入的,就令 (i) 为 (j) 的儿子,权值为 (x)。
可以发现这样的树,只需要满足:节点编号为 (0 sim N),节点权值为 (0 sim K),且孩子节点的编号和权值,要分别大于当前节点的编号和权值。
考虑令 (dp(i, j)) 表示,一棵 (i) 个点的树,节点编号为 (0 sim i - 1),根节点权值为 (j),且满足上述条件的方案数。
有转移:(displaystyle dp(i, j) = sum_{k = 1}^{i - 1} inom{i - 2}{k - 1} dp(i - k, j) sum_{j < x le K} dp(k, x))。
组合意义是:考虑编号为 (1) 的节点,它一定是根节点的孩子,且形成一个子问题,枚举 (1) 节点的子树大小,假设为 (k),将除了 (0, 1) 之外的 (i - 2) 个编号分配给除了 (1) 之外的 (k - 1) 个 (1) 的子树中的点,这里贡献一个 (displaystyle inom{i - 2}{k - 1});除了 (1) 子树外的大小为 (i - k),这里贡献一个 (dp(i - k, j));(1) 节点的权值为 ((j, K]) 中的整数,这里贡献一个 (displaystyle sum_{j < x le K} dp(k, x))。
后一个和式用后缀和优化,状态转移的总时间复杂度为 (mathcal O (N^2 K))。
答案就为 (dp(N + 1, 0))。
时间复杂度为 (mathcal O (N^2 K)),评测链接。
2020-02-10
cf538G
通过坐标变化,把曼哈顿距离转成切比雪夫距离。
也就是说四个方向 RLUD
,原本是横纵坐标选取其中一个加减 (1),现在变成可以分别确定横纵坐标是加还是减。
所以这就变成了一个一维的问题,好考虑一些。具体地说,只要把 ((x, y)) 变成 ((x + y, x - y)) 就行。
接下来考虑一维问题,只要分别解决了两个一维问题,显然就可以拼成答案。
假设操作序列为 (c),则 (c) 是个 (pm 1) 串。通过 (x_i gets (x_i + t_i) / 2),可以把 (c) 转成 (01) 串。
则我们考虑 (c) 是 (01) 串的情况,令 (s) 为 (c) 的前缀和。
则有 (s_i le s_{i + 1} le s_i + 1)。
考虑每个时刻,还有 (x_i = lfloor t_i / l floor s_l + s_{t_i mod l})。
令 (s = s_l),如果确定了 (s),就可以直接判断是否合法了。
具体的说,根据获得的 (n) 个时刻的信息,可以确定出 (s_i) 中一些位置的值了,然后根据 (s_i le s_{i + 1} le s_i + 1) 进一步判断。
但是现在不知道 (s),如何求出 (s) 呢。
我们把所有信息:(c_i s + s_{p_i} = x_i) 按照 (p_i) 从小到大排序。
((c_i = lfloor t_i / l
floor),(p_i = t_i mod l))
不过在排序之前,要在这些信息里加上两条:(0 s + s_0 = 0) 和 ((-1) s + s_l = 0)。
然后考虑相邻的两条信息,就可以列出不等式确定 (s) 的范围。
最后求得的 (s) 是一个区间,任取里面任何一个值都可以。
具体证明我也不太懂,反正 AC 了(
其它实现细节见代码。
时间复杂度为 (mathcal O (n log n + l)),评测链接。
agc035_d
可以发现 (A_1) 和 (A_N) 没啥用,不用考虑它们,然后最后加到答案里去就行。
那也就是说,现在有 (N - 2) 个数排成一行,你每次可以删掉其中一个,它就会加到左右两边去。
特别的,如果它在最左边,它就会被加到一个变量 (L) 上,如果它在最右边,它就会被加到一个变量 (R) 上。
最后要让 (L + R) 最小。
这时可以考虑倒着做。假设最后一个删掉的元素,是 (i)。
那么 (i) 左边的数,一部分扔到 (L) 里了,一部分扔到 (i) 上了,(i) 右边的数,一部分扔到 (R) 里了,一部分扔到 (i) 上了。
然后删除 (i) 本身,就有那些扔到 (i) 上的数,以及 (i) 本身,都会被加到 (L) 和 (R) 上。
那么我们假设,(i) 左边的数删除后,加到 (i) 上,总共加了 (x)。那么这 (x) 最后产生的贡献就是 (2x),因为加到了 (L) 和 (R) 上。
右边同理,只不过换了个方向,也是两倍贡献。
既然倒着考虑了,那就要想到区间 DP。我们观察 (i) 左边的区间,这段区间中的数,加到左边会对总答案贡献 (1) 倍,但是加到右边会贡献 (2) 倍。
于是定义如下状态:(dp(l, r, cl, cr)) 表示考虑区间 ([l, r]) 中的数,把它们删除后,会对总答案贡献 (cl cdot L + cr cdot R),要使这个贡献最小。
则最终答案为 (dp(2, N - 1, 1, 1) + A_1 + A_N)。
有转移:
特别地,如果 (l > r),则 DP 值为 (0)。
容易发现,DP 状态数的上界是 (mathcal O (N^2 2^N)),因为区间只有 (mathcal O (N^2)) 对,而后面的系数的增长可以看作一个 (N) 层的二叉树的形态。
经过一些精细计算,其实 DP 状态数和转移数只有 (mathcal O (2^N)),这里就略去不证了。
时间复杂度为 (mathcal O (2^N)),评测链接。
agc033_e
不失一般性,我们假设 (S[1] = exttt{R}),如果不成立则把两种颜色互换即可,不影响答案。
因为一开始就必须走红弧,所以环上不能出现相邻的蓝弧。也就是说,环上的每一段蓝弧的长度均为 (1)。
如果 (S) 全部都是 ( exttt{R}),则不需要其他限制了,答案就为 (2F_{N - 1} + F_N)((F) 为斐波那契数列,(F_0 = 0, F_1 = 1))。
否则至少有一个 ( exttt{B}),初始的一段 ( exttt{R}) 后必然会跟着一个 ( exttt{B}),所以那时必须要走到蓝弧旁边。
由此可得,一定不会出现一段连续的红弧,满足长度为偶数。也就是说每段连续的红弧的长度一定是奇数。
如果出现了长度为偶数的红弧,则如果一开始的位置在这段弧上,且到两端的蓝弧的距离的奇偶性和 (S) 中第一段 ( exttt{R}) 的长度不一样,就一定不可能在 ( exttt{R}) 结束时到达蓝弧旁边(注意到因为长度是偶数,才有到两端的蓝弧的距离的奇偶性相同。如果长度为奇数,能保证恰好有一端距离的奇偶性与 (S) 中第一段 ( exttt{R}) 的长度相同)。
进一步地,假设 (S) 中第一段 ( exttt{R}) 的长度为 (l_1),则每段红弧的长度,不能超过 ((l_1 + [l_1 mod 2 = 0]))(因为恰好有一端是可行的,到那一端的距离不能太大)。
接着,考虑 (S) 之后的每一段连续的 ( exttt{R})。
如果这一段 ( exttt{R}) 之后没有接着一个 ( exttt{B}),也就是说它在 (S) 的末尾,那么对它就没有限制。
否则,考虑它的长度,假设为 (l_i)。因为这一段 ( exttt{R}) 的上一个字符必然也是 ( exttt{B})(不是第一段了),所以一开始时必然是在某一段红弧的端点上。如果 (l_i) 为奇数的话,就要走到对面的端点上才行,否则一直在相邻的红弧上反复横跳就行。也就是说,如果 (l_i) 为奇数的话,这一段红弧的长度最长不能超过 (l_i);如果是偶数的话不产生影响。当然,因为起点是任选的,所以每一段红弧也都具有了这个性质。
总的来说,就是说每一段红弧的长度被限制了,最小是 (1),最大不妨令它为 (k),且要是奇数。然后要计算形成一个环的方案数。
我们把一段红弧,和它在逆时针方向的第一个蓝弧,算作一段,这样每一段的长度就必须是 (2 sim (k + 1)) 之间的偶数,所以 (N) 也必须是偶数。
环上的情况不好考虑,必须要转化成序列。
我们考虑第一个弧所在的那一段,假设那一段的长度为 (x),则有 (x) 种方法旋转,使得第一个弧仍然在那一段中。
那么也就是说,对于所有的权值是 (le k) 的正偶数,总和为 (N) 的序列 (A),每一个序列会为答案贡献 (A_1) 种方案。
也就是枚举 (A_1),然后计算总和为 (N - A_1) 的序列的数量,乘以 (A_1) 后贡献给答案。
总和为 (i) 的序列的个数很好算,只要枚举最后一个元素是多少就行了,有转移:(displaystyle f(i) = sum_{j = 2}^{k} [j mod 2 = 0] f(i - j))。
用前缀和优化一下就完事了。
时间复杂度为 (mathcal O (N + M)),评测链接。
2020-02-11
cf634F
如果固定上下边界,那么中间的点,按照纵坐标排序,就可以统计答案了。所以这样子是 (mathcal O (r^2 n)) 的。
那我们固定上边界,往下推下边界呢?那就是变成每次插入一个点,会影响它周围的 (k) 个点对答案的贡献。
但是动态插入,比较不好维护。那我们就从下往上推下边界,现在就变成删除,可以用链表快速定位和找周围的 (k) 个点。
时间复杂度为 (mathcal O (n log n + r^2 + r n k)),评测链接。
2020-02-12
cf666E
因为不会 SAM,所以用 SA。
我们考虑 Height 数组的 Kruskal 重构树,其实是和后缀树等价的。
那一次询问就是查询一棵子树中,出现次数最多的颜色,以及它出现的次数。
线段树合并即可(好像是我第一次写线段树合并)。
时间复杂度为 (mathcal O ((l + q) log m)),其中 (displaystyle l = |s| + sum |t_i|),评测链接。
cf611H
可以发现只要是无序对 ((a_i, b_i)) 相同的边,都可以只看做一类,这样就只有最多 (6 (6 + 1) / 2 = 21) 类边。
同时位数相同的点,也可以看作一类,这样只有最多 (6) 类点。
令 (m = lfloor log_{10} n floor + 1),点的类数就为 (m),边的类数就为 (m (m + 1) / 2)。
可以发现不同种类的点,不管怎么样也还是要连接起来的。
那么先特判一下 (m = 1) 的情况,然后可以证明一个结论:存在一种方案,满足:
- 每一类点都选出一个“关键点”,例如第 (i) 类点的关键点编号可以为 ({10}^{i - 1})。
- 只考虑这 (m) 个关键点,它们形成的导出子图,是一棵树,也就是一个 (m) 个点 (m - 1) 条边的树连接了所有“关键点”。
- 其它所有点只与“关键点”连接,也就是不存在“非关键点”之间的连边。
详细证明这里从略。
那么,我们考虑枚举连接所有“关键点”的树形态,比如使用 Prüfer 序列枚举,这部分的时间复杂度为 (mathcal O(m^{m - 2}))。
枚举完成后,对应种类的边就用掉了对应条,这里记得先扣掉。
然后考虑每一条边 ((u, v)),可以是 (u) 类“非关键点”连到 (v) 类“关键点”上,也可以是 (v) 类“非关键点”连到 (u) 类“关键点”上。
总之,一条边 ((u, v)) 将会把 (u) 类或者 (v) 类的“非关键点”的剩余个数减去 (1)。
这样一来就可以转化为一个带权(重数)二分图完美匹配的模型。
具体地说,左侧有 (m (m + 1) / 2) 个点,表示每一类边,右侧有 (m) 个点,表示每一类点。
左侧的每个点,带的权值(重数)为剩余的对应类型的边的数量。
右侧的每个点,带的权值(重数)为剩余的对应类型的点的数量。
不难发现这两个数量之和相等。左侧的每个点,假设它对应的边的类型为 ((u, v)),它向右侧的 (u, v) 类点对应的点分别连边。
使用网络流可以解决带重数的二分图匹配问题,这里写个 Dinic 水过去。
Dinic 跑完之后也可以直接构造方案了。
时间复杂度为 (mathcal O (m^{m + 4})),评测链接。
2020-02-13
cf613E
对于 (|w| le 2) 的情况,我们进行特判,这是为了之后写起来分类讨论可以简洁一些。
观察一下最终的行走方式可能会变成啥样:
很多情况下,会变成这个样子。
注意到可以分成三段,左边一个 U 形,中间一个只会向右走的形状,右边又一个 U 形。
啊,为啥说中间是只会向右走?因为我们是这么钦点的,当然也可能是从右边出发往左走,这时只要把 (w) 反转,再求一次就行。
我们可以处理出,从一个地方分段,会不会产生向左的,长度为某个值的 U 形。
这可以通过预处理 LCP 做到。
同理可以处理出,从一个地方分段,会不会产生向右的,长度为某个值的 U 形。
处理上述两个数组,它们就能够作为第一部分和第三部分。
我们把第二部分接在第一部分后,记 (f(i, j, k)) 表示,当前第二部分走到了 ((i, j)),并且匹配了 (w) 的前 (k) 个字符,并且不是从同一列走来的方案数。
类似地,记 (g(i, j, k)) 为必须从同一列走来的方案数。则 (f, g) 之间可以互相转移,这部分可以 (mathcal O (n |w|)) 处理。
然后考虑在 DP 到 ((i, j, k)) 时,接上第三部分即可,可以直接判断接不接得上。
当然还有一些其他情况没有讨论的,比如三个部分中的某一部分并不存在,甚至是两个部分不存在之类的,仔细讨论一下即可。
注意要不重不漏,特别注意 (w) 反转后不要统计重复了。
时间复杂度为 (mathcal O (n |w|)),评测链接。
cf566E
如果两个集合的交为 (2),则交出来的这两个点,之间一定有连边。
这样可以确定树中所有非叶子节点之间的连边情况。
先特判非叶子节点数为 (0, 1, 2) 的情况。
为 (0) 的话就是 (n = 2);为 (1) 的话就是菊花,每个集合大小都是 (n);为 (2) 的话就是有恰好两个集合大小是 (n)。
现在非叶子节点数至少是 (3)。那么我们需要确定每个叶子挂在了哪个非叶子上。
注意到我们在给非叶子节点连边的时候,就可以处理出每个非叶子节点,与其相连的非叶子节点的集合,也就是距离 (le 1) 的集合。
那么我们依次考虑每个叶子,它对应的集合就是所有集合中,包含这个叶子的,大小最小的那个。
在这个集合中,去掉所有叶子节点,就得到了与它相连的非叶子节点的邻接非叶子节点集合。
再枚举每个非叶子节点,就可以判断具体是和哪一个非叶子节点相连的了。
时间复杂度为 (displaystyle mathcal O !left( frac{n^3}{w} ight)),其中 (w) 为字长,评测链接。
cf573E
考虑一个贪心策略:一开始选空集,然后每次选一个不在集合中的数加入集合,具体选哪个数呢?选择让新的答案最大的数即可。
然后集合大小从 (0) 逐渐增大到了 (n),得到了 (n + 1) 个答案,我们选取其中最大的一个输出。
我们发现这样做成功 TLE 了(悲),但是并没有 WA,说明贪心是对的(确信)。
具体证明请下载 徐翊轩的题解 查看。
那么我们只需要快速维护这个贪心,具体地说,每个位置有一个权值 (b_i) 和一个固定的值 (a_i),需要支持四种操作:
- 前缀 (b_i) 加相同值。
- 后缀 (b_i) 加 (a_i)。
- 查询 (b_i) 的全局最大值。
- 删除一个位置。
一般线段树没法做,我们考虑分块。(其实这是一种经典分块类型)
每 (sqrt{n}) 个元素分一块,那对于每一块就要实现:
- 整体加。
- 整体加 (a_i)。
- 查询整体最大值。
- 重构。
可以发现大概是类似斜率优化那套式子,维护上凸壳即可。
注意到 (a_i) 始终不变,而要求的斜率不断递减,可以用单调队列维护,重构的时候也不用重新排序了。
本题还有 (mathcal O (n log n)) 的做法,在题解中同样可以看到。不过因为要手写平衡树,我比较懒就不写了。
时间复杂度为 (mathcal O (n sqrt{n})),评测链接。
2020-02-18
cf627F
设初始时 (0) 在 (s),目标时在 (t)。
注意到操作的本质是 (0) 的移动,同时操作具有可逆性。
因此,我们先不考虑次数最少的要求,先让 (0) 从 (s) 以最短距离移动到 (t)。
如果此时已经满足目标状态了,说明不需要加入新的边,此时 (0) 经过的距离即为最少步数。
否则,接下来我们只能加入一条新的边,然后让 (0) 从 (t) 出发,走到包含这条新边的环上距离 (t) 最近的点 (p),绕若干次完整的圈之后,回到 (t)。
观察这样操作对权值的变化可以得出,(t) 到 (p) 的路径上的所有点的权值都不会改变,同时每绕圈一次,环上除了 (p) 之外的点上的权值变化形成一个循环,而绕若干圈则为一个轮换。
因此,权值要改变的点加上 (p) 应该在树上形成一条路径。
由此我们能够确定连边 ((u,v)),可以 (t) 为根,找到所有权值需要改变的点,(p) 即为这些点中深度最小的点的父节点,而路径 ((u,v)) 则由 (p) 和这些点构成。
如果深度最小的点的父节点不唯一,或者 (p) 和这些点无法构成一条路径,或者这些点的权值不是目标权值的一个轮换,则说明无解。
最后来考虑最小化操作次数。
对于一条加边 ((u,v)),绕圈的方向有两种 (u o v) 和 (v o u),分别计算取 (min) 即可。
假设从 (u o v),由这个轮换对循环的次数 (c) 可以得到最小次数为 (2cdot operatorname{dist}(t, p) + c cdot (operatorname{dist}(u, v) + 1))。
但注意这个最小次数是在先将 (0) 从 (s) 移到 (t) 的前提下,因此如果有重复的路径需要减掉。
准确地说,如果是 (u o v),那就是将 (0) 直接从 (s) 经过树边移动到 (u),然后经过新加的边移动到 (v),然后在这个环上再绕 ((c - 1)) 圈回到 (v),最后经过树边移动到 (t)。此时的答案也就是 (operatorname{dist}(s, u) + (c - 1) cdot (operatorname{dist}(u, v) + 1) + operatorname{dist}(v, t) + 1)。
如果是 (v o u),只要把 (u, v) 互换,并重新计算循环的次数 (c) 即可。
时间复杂度为 (mathcal O (n)),评测链接。
2020-02-19
arc093_f
淘汰赛的比赛结构是一个完全二叉树,假设玩家 (1) 被安排在了某个位置,则他要最终获得胜利的话,需要打败 (N) 个对手。
这 (N) 个对手分别来自大小为 (2^0, 2^1, 2^2, ldots , 2^{N - 1}) 的子树中。
也就是说,它们是那些对应的子树中的最小值。
要让 (1) 取得胜利,这些值中不能有那 (M) 个 (A_i) 之一。
这相当于,把除了 (1) 以外的 (2^N - 1) 个数染上 (N) 种颜色,第 (i) 种颜色恰好要染 (2^{i - 1}) 个数。
而且对于每种颜色,最小的,染上这种颜色的数,不能是任何一个 (A_i)。
然后我们考虑容斥原理,假设一个集合中的 (A_i) 都必须是某个颜色的最小值。
可以发现让 (A_i) 从大到小 DP 会比较合适。记一个状态表示比当前值大的 (A_i) 被强制选取了哪些颜色的最小值,也就是说哪些颜色已经被用掉了。
转移的时候,该 (A_i) 可以不强制选,直接转移;或者强制选成某个还未被选择的颜色 (k) 的最小值,DP 值乘上 (displaystyle inom{s - 1}{2^{k - 1} - 1}),其中 (s) 表示后面还没有被选中的数的个数,(s) 可以直接由当前 (A_i) 和选取的颜色状态计算得出。
最后答案就是容斥得到的染色方案数,乘以 (displaystyle 2^N prod_{i = 1}^N (2^{i - 1})!),这是因为每个子树内可以任意排,然后 (1) 每次可以从左子树或右子树上来。
时间复杂度为 (mathcal O (M N 2^N)),评测链接。
2020-02-20
cf506E
不考虑题目中的“在原串中插入”,我们直接统计最终的长度为 (N = |s| + n) 的回文串的个数。
接下来的问题是:给定一个回文串,如何判断 (s) 是否作为一个子序列出现。
当然可以直接子序列自动机,但是这样子的性质不够好。考虑从 (s) 的两侧进行匹配。
假设当前回文串为 (t),我们使用 (t) 两侧的字符对 (s) 两侧的字符进行匹配:
假设 (t) 两端的字符为 (c),如果 (s) 左端的字符也为 (c),就删去这个字符,右边同理。
以 (s = mathtt{abaac}, t = mathtt{{color{red}b}{color{magenta}a}{color{blue}c}{color{Tan}b}{color{ForestGreen}a}{color{Tan}b}{color{blue}c}{color{magenta}a}{color{red}b}}) 为例:
- 用 (mathtt{{color{red}b}}) 去匹配 (s) 的两端,(s) 变为 (mathtt{abaac})。
- 用 (mathtt{{color{magenta}a}}) 去匹配 (s) 的两端,(s) 变为 (mathtt{{color{magenta}a}baac})。
- 用 (mathtt{{color{blue}c}}) 去匹配 (s) 的两端,(s) 变为 (mathtt{{color{magenta}a}baa{color{blue}c}})。
- 用 (mathtt{{color{Tan}b}}) 去匹配 (s) 的两端,(s) 变为 (mathtt{{color{magenta}a}{color{Tan}b}aa{color{blue}c}})。
- 用 (mathtt{{color{ForestGreen}a}}) 去匹配 (s) 的两端,(s) 变为 (mathtt{{color{magenta}a}{color{Tan}b}{color{ForestGreen}a}a{color{blue}c}})。
注意,这里只能匹配其中一个字符,因为 (oldsymbol{t}) 中只剩下一个 (mathtt{{color{ForestGreen}a}}) 了!
如果 (s) 少一个 (mathtt{a}) 或者 (t) 多一个 (mathtt{{color{ForestGreen}a}}),就能全部匹配。
如果按照这种方式全部匹配完了,就是合法的串。
由此我们可以构造一个 DP:(dp(x, i, j)) 表示确定了 (t) 的左右两端各 (x) 个字符后,恰好匹配到 (s) 中的子串 (s[i : j]) 的 (t) 的方案数。
并且一个特殊的状态 (dp(x, mathrm{done})) 表示匹配完了。
则答案就为 (displaystyle dp !left( leftlceil frac{N}{2} ight ceil!, mathrm{done} ight))。
对应的转移图如下:
但是因为 (N) 太大,没法直接这样做,直接做的复杂度是 (mathcal O ({|s|}^2 N)) 的。
观察到这个转移图,显然就是用来矩阵快速幂的,但是还是不行,复杂度是 (mathcal O ({|s|}^6 log N)) 的。
所以还是观察一下性质,比如我们可以发现,红色点就代表 (s) 的两端不同的情况,绿色点表示相同的情况。
那么要到达终点,如果经过了 (n_1) 个红色点,就必须要经过 (displaystyle leftlceil frac{|s| - n_1}{2} ight ceil) 个绿色点。
然后发现终点前的点一定是绿色点,所以最多经过 (|s| - 1) 个红色点,也就是说经过的红色点的数量在 (0) 到 (|s| - 1) 内。
我们单独把一条从起点到终点的链拿出来,可以发现,经过的红点和绿点的顺序实际上没有影响,也就是说把红点提前答案不变:
这个性质十分重要,因为本质不同的链的个数只有 (mathcal O (|s|)) 个,所以只要求出每种链的个数就行了,同样可以使用类似的 DP 得到。
这样的话,考虑在每一条链上做矩阵快速幂,得到的答案乘以链的条数再相加就是总答案,复杂度是 (mathcal O ({|s|}^4 log N)) 的。
我们可以更进一步优化,考虑这样的自动机(字符串长度为 (|s| = 5) 时,也就是和刚才举的例子相同):
其中 (g_0 sim g_{|s| - 1}) 分别表示经过的红点个数分别为对应值的链的个数。
可以发现恰好满足每一条本质不同的链都能够被表示出,而且不重复不遗漏。
在这个自动机上做矩阵快速幂就行了,因为加了一个空的起点,所以要多走一步。
但是这样直接求的话,对于 (N) 是奇数的情况会多算,就是前文提到的那种情况(最中心的字符只能匹配一个)。
我们这样考虑,先求出在 (displaystyle leftlfloor frac{N}{2} ight floor) 步内就能到达终点的方案数,乘以 (26)(还能再走一步)。
然后再加上在 (displaystyle leftlfloor frac{N}{2} ight floor) 步时恰好到达一开始的自动机中的 (s) 被删到长度为 (1) 时的节点的方案数。
计算第二种情况时,也要重新计算一下 (g_0 sim g_{|s| - 1}),最终就能求得总答案了。
时间复杂度为 (mathcal O ({|s|}^3 log N)),评测链接。
arc096_e
考虑容斥,枚举有 (a) 个只出现了一次,(b) 个一次都没出现。
则给答案贡献 (displaystyle {(-1)}^{a + b} inom{n}{a} inom{n - a}{b} 2^{2^{n - a - b}} sum_{x = 0}^{a} {a race x} {(2^{n - a - b})}^x)。
如果令 (c = a + b),变换为 (displaystyle {(-1)}^c inom{n}{c} 2^{2^{n - c}} sum_{x = 0}^{c} {(2^{n - c})}^x sum_{a = x}^{c} {a race x} inom{c}{a})。
考虑这个恒等式:(displaystyle sum_{i = x}^{n} {i race x} inom{n}{i} = {n + 1 race x + 1})。
所以答案为 (displaystyle sum_{c = 0}^{n} {(-1)}^c inom{n}{c} 2^{2^{n - c}} sum_{x = 0}^{c} {(2^{n - c})}^x {c + 1 race x + 1})。
时间复杂度为 (mathcal O (N^2 + N log M)),评测链接。
2020-02-21
cf575E
可以证明如下结论:
在平面直角坐标系中,给定若干个不全都共线的点。
要作一个半径尽量大的圆,使得该圆包含所有给定点,并经过至少三个给定点。
构造给定点构成的凸包,并要求凸包上不存在共线的三个顶点。由于这些点不全都共线,所以一定存在这样的凸包。
则有结论:要求的圆一定经过凸包上三个相邻顶点。
具体证明请下载 任清宇的题解 查看。
所以只要求出给出的点集的凸包后,枚举凸包上相邻三点计算并更新答案即可。
具体地说,由于每个人能够到达的点的凸包是一个点,或一个 (3 sim 6) 边形,只要求出每个人对应的凸包的顶点,这可以通过简单的讨论求出,再合并求一次大凸包即可。
时间复杂度为 (mathcal O (n log n)),评测链接。
cf607E
为了方便考虑,把坐标系平移到以 ((p, q)) 为原点处。
那么以原点为圆心作圆,只要找到圆内有 (m' < m) 个交点的最大半径即可。
那么答案就等于圆内交点到原点的距离之和,加上 (m - m') 倍的半径。
二分答案后考虑如何 check 是否有 (< m) 个交点。
把每个与圆有交的直线拿出来,就变成圆上的一条弦,对应了圆上极角序的一个区间。
就变成了对相交但不包含的区间对计数的问题,是二维偏序问题。
这部分时间复杂度为 (mathcal O (n log n (log v - log varepsilon)))。
确定了对应半径后,再使用类似方法在 (mathcal O (n log n + m)) 的时间内统计交点到原点的距离即可。
时间复杂度为 (mathcal O (n log n (log v - log varepsilon) + m)),评测链接。
2020-02-22
arc092_f
对于一条边 (u o v),将它反向后变成 (v o u),会对原图的强连通分量个数造成影响,当且仅当:
- 忽略这条边后,(u) 能直接或间接到达 (v)。
- 忽略这条边后,(v) 能直接或间接到达 (u)。
这两个条件仅恰好满足一个。证明不难,请自行脑补。
其中,忽略 (u o v) 后,询问 (v) 是否能够到达 (u),和不忽略其实也没啥区别,所以这部分可以直接做,(mathcal O (NM)) 的复杂度就可以接受了,当然你也可以用 bitset 做 (mathcal O (M + NM / w))。
然后考虑忽略 (u o v) 后,询问 (u) 是否能够到达 (v),也就是只要存在第一条边不走 (u o v) 的简单路径就行。
我们考虑对于所有的起点相同,也就是 (u) 相同的 (u o v) 计算这个东西,那么只要一次的时间复杂度为 (mathcal O (M)) 就可以接受了。
首先把 (u) 的出边排成一排,假设终点分别为 (v_1, v_2, ldots , v_k)。
那么先按照正序,也就是 (v_1, v_2, ldots , v_k) 的顺序进行 DFS,并记录每个点是从哪个点到达的(就是从哪个 (v_i) 出发),记做 (p(v_i))。
然后按照逆序,也就是 (v_k, v_{k - 1}, ldots , v_1) 的顺序进行 DFS,并记录每个点是从哪个点到达的,记做 (q(v_i))。
如果一个 (v_i) 可以从其它 (v_j)((j e i))出发到达它,当且仅当 (p(v_i) e q(v_i)),只要判断这个条件即可。
时间复杂度为 (mathcal O (NM)),评测链接。
2020-02-23
agc023_f
第一步,必须把根节点删掉。
然后可以发现,如果删掉一个节点之后,它的孩子中有 (0),那就可以立刻把孩子也删掉,这样答案不会变得更劣。
那我们把 (0) 和它的父亲并成一个连通块,表示这个连通块可以一次性取完,最后整棵树就变成了一些不相交连通块。
然后会发现,如果此时我们把每个连通块看成一个节点,还是一棵树的结构,但是这时每个连通块内就有若干个 (0) 和 (1) 混合了。
现在仅考虑新树的两个节点 (u, v),忽略其它的影响:
假设 (x) 中 (0, 1) 的个数分别为 (x_0, x_1),则如果 (u) 排在 (v) 前面,就会增加 (u_1 v_0) 对逆序对,反之增加 (v_1 u_0) 对。
如果 (u_1 v_0 < v_1 u_0),则 (u) 排在 (v) 前面肯定更优。
变换一下式子,变成 (displaystyle frac{u_1}{u_0} < frac{v_1}{v_0}),也就是连通块中 (1) 与 (0) 个数的比值。
考虑当前这个比值最小的连通块,假设为 (a),则可以发现当 (a) 的父亲被取到的时候,下一步一定会把 (a) 取了。
这是因为无论连通块怎么合并,这个比值都不会变得比原来更小,也就不会小于 (a) 的比值。
所以,拿一个支持插入删除,取出最小值的数据结构(比如 set),就可以维护了。
具体地说就是每次取出这个比值最小的连通块,把它的它的父亲合并。
时间复杂度为 (mathcal O (n log n)),评测链接。
2020-02-25
agc038_f
把 (P) 分解成不相交循环的乘积后,考虑其中一个循环 ((a_1, a_2, ldots , a_k))。
不失一般性,可以把这个循环看作 ((1, 2, ldots , k))。
那么对于 (A_1),有两种情况:(A_1 = 1) 或 (A_1 = P_1 = 2)。
如果 (A_1 = 1),则考虑 (A_k) 有两种情况:(A_k = k) 或 (A_k = P_k = 1),但是因为 (A_1 = 1),所以只能有 (A_k = k)。
以此类推,可以得到:对于这个循环中的所有元素 (i),均有 (A_i = i)。
如果 (A_1 = 2),则考虑 (A_2) 有两种情况:(A_2 = 2) 或 (A_2 = P_2 = 3),但是因为 (A_1 = 2),所以只能有 (A_2 = 3)。
以此类推,可以得到:对于这个循环中的所有元素 (i),均有 (A_i = P_i)。
换句话说,对于每个循环,要么这个循环被完全保留,要么这个循环被完全拆解成一个个自环。
上述结论对 (Q) 和 (B) 当然也适用。
我们称选择一个循环,指这个循环被完全保留,称不选一个循环,指这个循环被拆解成了一个个自环。
接着,考虑一个 (P) 中的循环 (a) 和一个 (Q) 中的循环 (b),假设它们共有一个元素 (i)。分若干类讨论:
- (P_i = Q_i = i):无论如何,这个位置上均有 (A_i = B_i)。
- (P_i = i, Q_i e i):如果选择了 (b),则这个位置上有 (A_i e B_i),否则不选 (b),则这个位置上有 (A_i = B_i)。
- (P_i e i, Q_i = i):如果选择了 (a),则这个位置上有 (A_i e B_i),否则不选 (a),则这个位置上有 (A_i = B_i)。
- (P_i e i, Q_i e i, P_i e Q_i):如果不选 (a) 且不选 (b),则这个位置上有 (A_i = B_i),否则这个位置上有 (A_i e B_i)。
- (P_i e i, Q_i e i, P_i = Q_i):如果 (a, b) 同时选择或同时不选,则这个位置上有 (A_i = B_i),否则这个位置上有 (A_i e B_i)。
最终需要最大化 (A_i e B_i) 的下标 (i) 的数量,也就是最小化 (A_i = B_i) 的下标 (i) 的数量。
如果在上述 (5) 种情况中,一旦发生了 (A_i = B_i),就赋有 (1) 的代价,那么就是要最小化总代价。
可以发现类似于一个文理分科模型,可以建立一个网络流模型,求最小割得到答案。
但是因为有些条件不符合,没法直接套用。
不过,如果把 (Q) 中的循环割掉与源点和汇点之间的边的意义交换,就可以套用了。
而且可以发现,这样建出来的图是一个二分图,因为 (P) 中的循环只和源点连边,(Q) 中的循环只和汇点连边,(P, Q) 之间也只会互相连边。(如果 (P) 中的循环对应的节点,割掉与源点相连的边的意义是不选它,而 (Q) 中的循环对应的节点的意义恰好相反的话)
所以最终是在单位容量的二分图上求最小割,使用 Dinic 算法可以做到 (mathcal O (|E| sqrt{|V|})) 的复杂度。
时间复杂度为 (mathcal O (N sqrt{N})),评测链接。
agc023_d
如果 (X_1 < S < X_N),考虑第 (1) 栋楼和第 (N) 栋楼。
如果 (P_1 ge P_N),即第 (1) 栋楼中的人数大于等于第 (N) 栋楼中的人数,则班车一定会先去第 (1) 栋楼。证明:
- 如果 (N = 2),显然成立。
- 如果 (N ge 3) 且 (X_{N - 1} < S),显然除了第 (N) 栋楼的员工,都希望前往负方向,所以一定会前往负方向。
- 如果 (N ge 3) 且 (S < X_{N - 1}),如果在到达第 (1) 栋楼之前没有到达第 (N - 1) 栋楼,则结论成立,否则转化为前两种情况。
所以说不管怎么样都会先前往第 (1) 栋楼,然后就可以一路向右径直跑到第 (N) 栋楼。
这就意味着,第 (N) 栋楼中内的员工的回家时间,一定等于第 (1) 栋楼的回家时间,加上 (X_N - X_1)。
也就是说,第 (N) 栋楼中的员工,其实是和第 (1) 栋楼中的员工站在同一条线上的。第 (1) 栋楼的员工想投什么票,他们也一定会跟着投。所以说这第 (N) 栋楼的员工其实和第 (1) 栋楼的员工没什么区别,暂时(在第 (1) 栋楼的员工回家之前)让他们搬家到第 (1) 栋楼也对运行路径没有影响。
所以说,如果让 (P_1 gets P_1 + P_N),然后删去第 (N) 栋楼,计算这种情况下的到达第 (1) 栋楼的时间,加上 (X_N - X_1) 就是答案。
如果 (P_1 < P_N),那么以上结论的方向反过来即可。
这样递归下去,直到不满足 (X_1 < S < X_N) 为止,那样的话就可以直接计算答案了。
时间复杂度 (mathcal O (N)),评测链接。
2020-02-26
agc036_d
考虑差分约束模型,图中不存在负环等价于存在一组合法的差分约束的解。
考虑每个节点作为一个变量,第 (i) 个节点对应的变量为 (x_i)。
因为初始的边不能删去,所以一定有 (x_i ge x_{i + 1})。
考虑令 (q_i = x_i - x_{i + 1}),那么就会有 (q_i ge 0)。
假设保留了一条边权为 (-1) 的 (i o j) 的边,也就是说 (i < j) 的话:
就会有 (x_i - 1 ge x_j),即 (x_i - x_j ge 1),也就是说 (q_i + q_{i + 1} + cdots + q_{j - 1} ge 1)。
假设保留了一条边权为 (1) 的 (i o j) 的边,也就是说 (i > j) 的话:
就会有 (x_i + 1 ge x_j),即 (x_j - x_i le 1),也就是说 (q_j + q_{j + 1} + cdots + q_{i - 1} le 1)。
反过来想,如果确定了所有的 (q_i),那么每一条边就应该尽可能地保留下来,这样代价最小。
对于边权为 (-1) 的边,是区间和 (ge 1) 才能保留,也就是说如果区间和 (= 0) 就必须删除。
对于边权为 (1) 的边,是区间和 (le 1) 才能保留,也就是说如果区间和 (ge 2) 就必须删除。
也就是说,对于一种 (q) 的取值方案,(q_i = 0) 的每个连续段,都对应着一系列的边权为 (-1) 的边的删除。
而区间和 (ge 2) 的区间也对应着边权为 (1) 的边的删除。
显然可以发现,如果出现了 (q_i ge 2),不如把它变成 (1),这样一定会变得更优(边 (i o (i + 1)) 不用被删除了)。
所以只需要考虑 (q) 的取值为 ({0, 1}) 的情况。
然后可以发现,每个 (0) 的连续段就对应着一部分区间的删除,所以考虑如下 DP:
记 (dp(i, j)) 表示考虑到了 (q_i),最后一个 (1) 取在了 (q_i),倒数第二个 (1) 取在了 (q_j) 处的情况下,可以确定的代价的最小值。
(dp(i, j)) 可以从 (dp(j, k)) 转移而来,利用二维前缀和可以快速求出转移系数。
时间复杂度为 (mathcal O (N^3)),评测链接。
2020-02-27
agc026_e
令 (a_i) 表示第 (i) 个 (mathtt{a}) 的位置,(b_i) 表示第 (i) 个 (mathtt{b}) 的位置。
考虑如何比较两个字符串的字典序,可以发现当某个前缀相同时应该比较后缀,所以考虑从后往前 DP:
令 (dp(i)) 表示只考虑所有的 (a_{i sim N}) 和 (b_{i sim N}),也就是第 (i) 对以及之后的 (mathtt{a}, mathtt{b}) 的情况下的字典序最大的串。
注意不是第 (i) 对 (mathtt{a}, mathtt{b}) 以及它们之后的所有字符都一定选择,而是一对一对的选择的。
那么答案就为 (dp(1))。而 (dp(i)) 可以从两个方向转移,也就是 (a_i) 和 (b_i) 保留或者删掉。
如果删掉,就直接从 (dp(i + 1)) 转移而来。
否则考虑如果保留第 (i) 对 (mathtt{a}, mathtt{b}) 的话会怎么样,根据先后顺序分成两类讨论:
- (a_i < b_i):也就是形如 (cdots mathtt{{color{red}a}{color{blue}a}{color{green}b}{color{blue}a}{color{blue}a}{color{green}b}{color{red}b}} cdots) 的情况。
红色的字符就是第 (i) 对 (mathtt{a}, mathtt{b}),绿色的字符表示第 (i) 对之前的字符,蓝色的字符表示第 (i) 对之后的字符。
注意绿色的字符只可能是 (mathtt{b}),而蓝色的字符只可能是 (mathtt{a})。因为绿色的字符不会被保留,之后忽略它们。
既然已经确定了必须选取 (a_i, b_i),因为要让字典序尽量大,所以 (a_i) 到 (b_i) 之间所有的 (mathtt{a}) 都应该被删掉。
也就是说,(dp(i)) 就应该等于 (mathtt{ab} + dp(k)),其中 (k) 为完全在 (b_i) 之后的第一对 (a_k, b_k) 的编号。 - (a_i > b_i):也就是形如 (cdots mathtt{{color{red}b}{color{blue}b}{color{green}a}{color{blue}b}{color{blue}b}{color{green}a}{color{red}a}} cdots) 的情况。
红色的字符就是第 (i) 对 (mathtt{a}, mathtt{b}),绿色的字符表示第 (i) 对之前的字符,蓝色的字符表示第 (i) 对之后的字符。
注意绿色的字符只可能是 (mathtt{a}),而蓝色的字符只可能是 (mathtt{b})。因为绿色的字符不会被保留,之后忽略它们。
既然已经确定了必须选取 (a_i, b_i),因为要让字典序尽量大,所以 (a_i) 到 (b_i) 之间所有的 (mathtt{b}) 都应该被保留。
而确定要保留这些 (mathtt{b}),又会导致往后包含了更多的 (mathtt{b}),同理被包含的 (mathtt{b}) 也应该被保留,连锁反应会一直进行下去,直到某一次不包含了更多的 (mathtt{b}) 为止。举个例子:
考虑 (mathtt{{color{blue}b}b{color{blue}a}babbbabaaaabbabaaaabb}),
选取 (mathtt{{color{red}b}{color{blue}b}{color{red}a}b{color{blue}a}bbbabaaaabbabaaaabb}),
选取 (mathtt{{color{red}{bba}}{color{blue}b}{color{red}a}bbb{color{blue}a}baaaabbabaaaabb}),
选取 (mathtt{{color{red}{bbaba}}{color{blue}{bbb}}{color{red}a}b{color{blue}{aaa}}abbabaaaabb}),
选取 (mathtt{{color{red}{bbababbba}}{color{blue}b}{color{red}{aaa}}{color{blue}a}bbabaaaabb}),
选取 (mathtt{{color{red}{bbababbbabaaaa}}bbabaaaabb})。
在这种情况下,(dp(i) = mathtt{bbababbbabaaaa} + dp(k)),其中 (k) 为后面部分的第一对 (a_k, b_k) 的编号。
所以只要求出以上两类的结果就行,第 1 类可以预处理,第 2 类的开头的字符串,可以直接扫一遍判断。
时间复杂度为 (mathcal O (N^2)),评测链接。
cf679E
注意到在可能的值域(约为 ({10}^{14}))内,(42) 的次幂并不多,尝试从这个角度考虑。
操作 3 比较棘手,解决的办法是用线段树维护当前值到下一个 (42) 的次幂的差值。
做操作时让这个差值做区间减法,在线段树递归的时候,如果差值会变成负数,就需要再递归进子区间进行修改,但是如果这个区间被打上了区间覆盖标记,就直接修改这个标记就行。
执行完后,如果存在差值为 (0) 的位置,就再执行一次。
这个做法的复杂度,使用势能函数可以分析得出为 (mathcal O (q log n log_{42} v))。
具体地说,令当前线段树的势能函数等于每个值相同的连续段,比此连续段的值大的,在值域内的 (42) 的次幂的个数的总和,乘以 (log n)。
则操作 2 和操作 3 的摊还代价都为 (mathcal O (log n log_{42} v))。
时间复杂度为 (mathcal O ((n + q) log n log_{42} v)),其中 (v) 为值域,约为 ({10}^9 q),评测链接。
2020-02-28
agc039_e
令 (n = 2 N),枚举第 (n) 个点和哪个点连了,假设为 (k),即:
就分成了 (1 sim (k - 1)) 和 ((k + 1) sim (n - 1)) 两段。
直接考虑如果是区间 ([i, j]),且这区间中的一点 (k) 与区间外的一点连线了,即:
如果 (i < j),那么被 (k) 分割的左右两边必然要通过至少一条线与 ((? leftrightarrow k)) 连接起来
但是又不能交叉,如果交叉就形成环了,所以取最上方的一条线 ((x leftrightarrow y))。
所谓最上方,形式化地说就是 (x) 最靠近 (i),(y) 最靠近 (j)。
那么,(x, y) 在两边必然就会有各自的“管辖范围”。
(你可以理解成,从 ((? leftrightarrow k)) 和 ((x leftrightarrow y)) 的交点出发向 (x) 或 (y) 方向走,能遍历到的区域,它和其它区域不相交)
假设这个范围分别为 ([i, p]) 和 ([q, j])。
那么如果我们枚举 (i, j, k, x, y, p, q)(满足 (i le x le p < k < q le y le j)):
就可以转化成三个子问题 ([i, p](x)) 和 ([p + 1, q - 1](k)) 和 ([q, j](y))。
可以在 (mathcal O (n^7)) 的复杂度内解决此问题,因为常数大约是 (1 / 7! = 1 / 5040),所以其实是可以过的。
不过可以继续优化,可以发现 ([i, p]) 和 ([q, j]) 是和 (k) 独立的,也就是如果 ([i, j]) 固定,(k) 的位置不影响 (p, q) 的选择。
那么我们考虑先枚举 (p, q),得到 ([i, p] circ [q, j]) 这个子问题,再在子问题里枚举 (x, y)。
则处理所有 ([i, q] circ [q, j]) 就可以做到 (mathcal O (n^6)) 的复杂度(枚举 (6) 个变量)。
外层的 ([i, j](k)) 就可以只枚举 (p, q) 进行转移,这部分复杂度为 (mathcal O (n^5))。
总时间复杂度为 (mathcal O (n^6)),同样带了一个 (1 / 6! = 1 / 720) 的常数。
不过可以继续优化,现在复杂度瓶颈是在 ([i, p] circ [q, j]) 这里,需要枚举 (x, y) 才能转移。
如果只枚举一个 (y) 呢?
那就需要求 ([i, p]) 区间中的,从 (y > p) 连进来一条边的方案数,用记号 ([i, p]{y}) 表示。
当然还有本来就要求的 ([q, j](y)),这个是旧的东西了。
那么考虑计算 ([i, p]{y}),这时就可以枚举具体是和哪个 (i le x le p) 连边,然后直接加上 ([i, p](x)) 即可。
所以处理所有 ([i, p]{y}) 的复杂度为 (mathcal O (n^4)),而处理所有 ([i, p] circ [q, j]) 的复杂度降为 (mathcal O (n^5))。
总时间复杂度为 (mathcal O (n^5)),带了一个 (1 / 5! = 1 / 120) 的常数,评测链接。