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)),轻微卡常。