联合省选 2021 B 卷题解

Day1

数对

给定 (n) 个正整数 (a_i),请你求出有多少个数对 ((i, j)) 满足 (1 le i le n)(1 le j le n)(i e j)(a_i)(a_j) 的倍数。

(2 le n le 2 imes 10^5)(1 le a_i le 5 imes 10^5)

考虑使用桶,记 (cnt_x) 表示 (a_i = x) 的个数。

那么分两种情况讨论,第一种情况是 (a_i = a_j = x),对答案的贡献为 (sum inom{cnt_x}{2});第二种是 (a_i = x < a_j = y),那么这时候枚举 (a_i) 的值 (x),同时枚举 (x) 的倍数 (y),对答案的贡献为 (sumlimits_{x} sumlimits_{y} cnt_x cnt_y)

时间复杂度是经典的调和级数 (O(frac{n}{1} + frac{n}{2} + frac{n}{3} + cdots) = O(n ln n))

卡牌游戏

(n) 张卡牌,第 (i) 张卡牌的正面有数字 (a_i),背面有数字 (b_i),初始时所有卡牌正面朝上。

现在可以将不超过 (m) 张卡牌翻面,最小化朝上的 (n) 个数字的极差。

(3 le n le 10^6)(1 le m < n)(1 le a_i, b_i le 10^9)(a_i) 单调不下降,(2n) 个数字互不相同。

先把 (a, b) 拼成一个长度为 (2n) 的数组 (c) 排序。

对于这个有序的数组,极差即是选中的那一段 (l, r)(c_r-c_l)

于是用双指针枚举 (l, r),不在 (l, r) 当中则意味着那一位被删除,具体要求是:

  • 删掉的 (a) 面不超过 (m)
  • 不存在同一张卡牌的 (a, b) 面同时被删除。

对于第一条开一个 (cnt) 维护,第二条开一个 (vis) 数组维护即可。

时间复杂度 (O(n log n)),不明白 (a) 有序有什么意义。

图函数

对于一张 (n) 个点 (m) 条边的有向图 (G),定义函数 (f(u, G))

  • 初始化返回值 (cnt = 0),图 (G' = G)
  • (1)(n) 按顺序枚举顶点 (v),如果当前的图 (G') 中,从 (u)(v) 与从 (v)(u) 的路径都存在,则将 (cnt + 1),并在图 (G') 中删去顶点 (v) 以及与它相关的边。
  • (2) 步结束后,返回值 (cnt) 即为函数值。

现在给定一张有向图 (G),请你求出 (h(G) = f(1, G) + f(2, G) + cdots + f(n, G)) 的值。

更进一步地,记删除(按输入顺序给出的)第 (1)(i) 条边后的图为 (G_i),请你求出所有 (h(G_i)) 的值。

(2 le n le 10^3)(1 le m le 2 imes 10^5)(1 le x_i, y_i le n),没有重边和自环。

因为只要求求出每个 (h(G_i)),而不是求具体的 (f(u, G_i)),所以考虑贡献法。

考虑一对 ((u, v)) 如果能对 (f(u, G_i)) 造成贡献,当且仅当存在一条 (u o v o u) 的环,且这个环上没有编号 (<v) 的点。

于是问题转化为对于 (forall G_i),求 (G_i) 中有多少 ((u, v))(u ge v))满足存在 (u o v o u) 的环不经过编号 (< v) 的点。

一个点对显然会对一个 (G) 的前缀造成贡献,令 (path(u, v)) 表示最大化的 (u o v) 的路径上经过的边的编号的最小值。那么 (G_{1 sim min(path(u, v), path(v, u))}) 会得到贡献。

可以直接 Floyd (O(n^3 + m)) 或 Dijkstra (O(nm log m)) 求出。

当然这不稳,可以考虑一个更高明的做法。枚举 (v),在每张图中只保留 ([v, n]) 那些点,求出每张图中它能到达的点的个数即可。

当然直接这样模拟是 (O(nm^2)) 的,考虑反转时间轴变成逆序加边,对于一条新加的边 ((a, b))(a, b ge v)):

  • 如果 (vis_a = 1)(vis_b = 0),那么从 (b) 开始 BFS;
  • 如果 (vis_a = 0)(vis_b = 0),那么先加到图中,等着以后可能有用;
  • 否则这条边没有意义。

易证每一条边最多只会用到一次,所以在 BFS 的时候“过河拆桥”即可。时间复杂度 (O(nm))

Day2

取模

给定 (n) 个正整数 (a_i) 请你在其中选出三个数 (i, j, k)(i e j)(i e k)(j e k)),使得 ((a_i + a_j) mod a_k) 的值最大。

(3 le n le 2 imes 10^5)(1 le a_i le 10^8)

首先将 (a) 数组排序,逆序枚举 (a_k),在前面做二分或者双指针求出 ((a_i + a_j) mod a_k) 的最大值更新答案。

(ans ge a_k) 的时候就可以 break 了,可以证明枚举的 (a_k) 的个数是 (log) 级别的,但我不会证

宝石

给定一棵大小为 (n) 的树以及 (m) 种宝石,结点 (i) 上有第 (w_i) 种宝石。

宝石收集顺序是一个长度为 (c) 的序列 (P)(P) 中没有重复元素。

(q) 次询问,每次给出 (s, t),问从 (s) 走到 (t) 的过程中,所经过的结点的 (w) 形成的序列与 (P) 的最长公共子序列的长度。

(1 le n, q le 2 imes 10^5)(1 le c, m le 5 imes 10^4)(1 le P_i, w_i le m)

(s o t) 拆为 (s o ext{lca}, ext{lca} o t) 的两个过程。

考虑链上的情况,记结点 (u) 的后继 (suc_{u}) 为其右边最近的满足 (w_v)(P) 序列中恰好是 (w_u) 的下一项的 (v)

那么如果询问的 (s < t),就从 (s) 右边的第一个 (w = P_1) 的位置开始,不断跳 (suc),直到越过 (t) 结束,跳的次数就是询问的答案。不难发现这个过程可以倍增优化。

现在搬到树上,(s o ext{lca}) 的过程不难套用上述方法,但如果考虑 ( ext{lca} o t) 呢?

一个解决办法是把过程拟过来,记录 (pre_{u}) 为其父亲链上的最近的满足 (w_v)(P) 序列中恰好是 (w_u) 的前一项的 (v)

每次询问的时候二分答案 (a),从 (t) 上方的最近的 (w = P_a) 的那一个位置开始不断跳 (pre) 就行了。

至于找到“(t) 上方的最近的 (w = P_x) 的位置”,可以使用可持久化线段树维护这个东西。

时间复杂度 (O(q log^2 m))

滚榜

给定一个长度为 (n) 的数组 (a),求有多少 (1 sim n) 的排列 (p),满足存在一个 (sum b = m) 的不下降序列 (b),使得对于任意 (i, j)(i < j)),都有:

  • (a_{p_j} + b_{p_j} ge a_{p_i} + b_{p_i} + [p_i < p_j])
  • (a_{p_i} + b_{p_i} ge a_{p_j} + [p_j < p_i])

(1 le n le 13)(1 le m le 500)(0 le a_i le 10^4)

容易发现 (b) 的分配是贪心的,每次给出最小的 (b),只要 (sum b le m) 就是合法的方案。

一个显然的结论是“当前选手想当 rank 1 只要比上一名选手高即可”,这是因为上一名选手是当前的 rank 1。

于是可得(忽略编号问题,这个显然是容易处理的):

  • 如果 (a_i ge a_{i - 1}),则 (b_i = b_{i - 1})
  • 如果 (a_i < a_{i - 1}),则 (b_i = b_{i - 1} + a_i - a_{i - 1})

综合一下上面两个式子其实就是 (b_{i} = b_{i - 1} + max(0, a_i - a_{i - 1}))

进而通过考虑贡献可以知道 (sum b = sum max(0, a_i - a_{i - 1}) (n - i + 1))(每个贡献都是一个后缀)。

于是考虑状压 DP,用 (f(S, i, j)) 表示,当前使用过的集合是 (S),最后一个选的是 (i),以及当前对 (sum b) 的贡献是 (j)。转移的时候枚举接下来选哪一个人,计算一下贡献即可。

时间复杂度 (O(2^n n^2 m)),轻微卡常。

原文地址:https://www.cnblogs.com/syksykCCC/p/AHOI2021.html