AtCoder 竞赛典型 90 题,解题报告
提示:每题括号内的数字大致表示难度。
001 - Yokan Party(★4)
题意
有一个长度为 $L$ 的纸条。在 $N$ 个位置 $a_1,a_2,...,a_n$ 找 $K$ 个分割点,最大化最短段的长度。
$1 \le K \le N \le 10^5$,$0<a_1<a_2<...<a_n<L \le 10^9$。
解析
二分最短段的长度 $l$,贪心来强制每段 $\ge l$,检验是否能凑出 $\ge k+1$ 段。
代码:https://atcoder.jp/contests/typical90/submissions/35153678
002 - Encyclopedia of Parentheses(★3)
题意
按照字典序打印所有长度为 $n$ 的合法括号序列。
$1 \le n \le 20$。
解析
$n$ 是奇数就不输出,否则可以 DFS 解决。
代码:https://atcoder.jp/contests/typical90/submissions/35154547
003 - Longest Circular Road(★4)
题意
给定一棵 $n$ 个节点的树,加一条边使得构成的环包含点数最多。
$1 \le n \le 10^5$。
解析
这个问题等价于求树的直径。(即距离最远的两点的路径经过的点数)
两种解法:
- 利用贪心的想法。先找到距离节点 $1$ 最远的点 $p$,再找到距离 $p$ 最远的点。执行两次 DFS 即可。
- 假设点 $1$ 为根,对于每个节点 $x$,找到其子树中最长的两条能接上 $x$ 的链(不能有交点),把 $x$ 接上去计算答案。
代码:
- 做法一:https://atcoder.jp/contests/typical90/submissions/35394771
- 做法二:https://atcoder.jp/contests/typical90/submissions/35394830(这个做法本来是 $\mathcal{O}(n)$ 的,但是代码偷懒用了排序)
004 - Cross Sum(★2)
题意
给定一个 $H \times W$ 的矩阵,对于每个位置 $(i,j)$ 求出所有在第 $i$ 行或第 $j$ 列的元素和。
$1 \le H,W \le 2000$,矩阵元素是 $[1,99]$ 间的整数。
解析
建立分别记录每行、每列元素和的数组,在输入的时候,就将每个数存入对应的位置,最后对于每个位置就可以 $\mathcal{O}(1)$ 查询。
代码:https://atcoder.jp/contests/typical90/submissions/35395190
005 - Restricted Digits(★7)
题意
(题目已运到 YZOJ 5970)
有多少个 $N$ 位十进制数,满足每位数字都在集合 $S$ 中,且它是 $B$ 的倍数?答案对 $10^9+7$ 取模。
任务一:解决 $N \le 10000, B \le 50$。
任务二:解决 $N \le 10^{18}, B \le 50$。
任务三:解决 $N \le 10^{18}, B \le 1000$。
集合 $S$ 内只有 $1 \sim 9$ 间的整数。
解析
对于任务一,我们设 $dp_{i,j}$ 表示目前已经确定 $i$ 位,当前数字除以 $B$ 余 $j$。直接转移是 $\mathcal{O}(NB^2)$ 的。
对于任务二,我们发现 $dp_{i,j}$ 只与 $dp_{i-1,k}$ 有关,而且第二维大小不超过 $50$,可以考虑矩阵加速。时间复杂度 $\mathcal{O}(B^3 \log N)$。
任务三尚待补充……
006 - Smallest Subsequence(★5)
题意
有一个长度为 $N$ 的小写字符串 $S$,请从中找到长度为 $K$ 且字典序最小的子序列 $T$。
$1 \le K \le N \le 10^5$。
解析
根据字典序的策略,只要能保证能凑满 $K$ 个字母,那尽可能要保证越靠前的字母,它越小。
所以假设当前我们将 $T_p$ 定为了 $S_q$,那么相当于剩余的 $N-q$ 字母,我们要挑 $K-p$ 个出来。
那么接下来要填的那个字母可能分布在哪里?在区间 $[N-q, \ N-q-(K-p-1)]$。这是因为我们要保证还没被挑的 $K-p-1$ 个字母有位置。
剩下的任务,就是在区间 $[N-q, \ N-q-(K-p-1)]$ 内找到最小的字母,同时保证它下标尽量小(为了让后面的挑选范围尽量大)。
这个任务可以使用多标记的线段树。标记存两个东西:区间最小的字母,以及它所在的原串下标(且是尽可能小的)。
细节就不多说了,可以看这份代码:https://atcoder.jp/contests/typical90/submissions/36932587
007 - CP Classes(★3)
题意
数轴上有 $n$ 个点 $a_1,a_2,...,a_n$,有 $q$ 个询问,每次输入一个位置 $x$,求出距 $x$ 最近的点到它的距离。
$N,Q \le 3 \times 10^5$,$0 \le a_i,x \le 10^9$。
解析
可以二分,也可以排序 + 双指针。注意边界情况。
代码:https://atcoder.jp/contests/typical90/submissions/36932727
008 - AtCounter(★4)
题意
给一个长为 $N$ 的小写字母串。求有多少个不同的子序列,等于 "atcoder"。
$N \le 10^5$。
解析
观察到 atcoder 这七个字母互不相同,可以设 $dp_{i,0/1/2/3/4/5/6}$ 代表枚举到第 $i$ 位且分别以 $a,t,c,o,d,e,r$ 结尾的字符串个数。
转移方程:$dp_{i,j}=\sum dp_{i-1,j-[t_j=s_i]}$。时间复杂度 $\mathcal{O}(n)$。
代码:https://atcoder.jp/contests/typical90/submissions/36932826
011 - Gravy Jobs(★6)
题意
有 $n$ 项工作。第 $i$ 项工作需要在第 $d_i$ 天及更早结束,一次耗时 $c_i$ 天(不能间断)。完成后可以获得报酬 $s_i$。
如果一个人一天只能做一项工作,请求出最大报酬。
$1 \le n,d_i,c_i \le 5000$,$1 \le s_i \le 10^9$。
解析
根据贪心策略,越早截止的工作,应尽早开始完成。所以我们先按照 $d_i$ 升序排序。
如果没有截止日期的限制,这就是一个背包问题(截止日期为容量)。
现在加上这个限制,由于工作已经被排序,我们可以认为每次新加入一个工作,背包就会被扩容。
然后就和普通的背包没什么区别了。
代码:https://atcoder.jp/contests/typical90/submissions/36933185
015 - Don't be too close(★6)
题意
给定 $n$。你将得到一个序列 $1,2,...,n$。对于每个 $x=1,2,3,...,n$,求出有多少个子序列满足序列内的元素两两相减绝对值均 $\ge x$。
答案对 $10^9+7$ 取模,$1 \le n \le 10^5$。
解析
如果直接动态规划,时间复杂度是 $\mathcal{O}(n^2)$ 的,过不了。那么考虑问题的组合数学意义!
对于每个 $x \ (x \ge 1)$:
- 我们枚举 $t=1 \sim \lfloor\frac{n}{x}\rfloor$ 代表子序列包含的元素个数。
- 容易发现会有 $(x-1)(t-1)$ 个空隙是不能填数字的,我们将其抽走,那么序列只剩 $n-(x-1)(t-1)$ 个位置,供这 $t$ 个数选取位置。
- 所以对于每个 $x$,答案即为 $\sum\limits_{i=1}^{\lfloor\frac{n}{x}\rfloor}\binom{n-(x-1)(i-1)}{i}$。
由于 $\sum\limits_{i=1}^{+∞}\frac{n}{i}= \ln n$,因此预处理组合数的情况下,时间复杂度可以做到 $\mathcal{O}(n \ln n)$。
代码:https://atcoder.jp/contests/typical90/submissions/37983375
016 - Minimum Coins(★3)
题意
有三种面值的硬币 $A,B,C$。问最少多少个硬币可以支付恰好 $N$ 元。
$1 \le A,B,C,N \le 10^9$,答案 $\le 9999$。
解析
直接枚举 $A,B$,可以间接求出 $C$,然后比较答案。
代码:https://atcoder.jp/contests/typical90/submissions/38166776
019 - Pick Two(★6)
题意
有一个长为 $2n$ 的序列 $a_1,a_2,...,a_{2n}$。每次选中当前相邻的两个数 $a_i,a_{i+1}$,删除它们,代价是 $|a_i-a_{i+1}|$。
每次把两个数删除后,剩下的序列会拼在一起。问删光序列的最小代价和。
$n \le 200$,$a_i \le 10^6$。
解析
每次都是删除当前序列的两个相邻位置的数,那么如果原序列两个数正在被删除,就代表它们之间的数已经删光了。
所以可以上区间 DP。$dp_{i,j}$ 表示将序列子区间 $[i,j]$ 删空的最小代价。
$dp_{i,j}$ ($j-i+1$ 为偶数且大于等于 $4$)有两种构成方式:
- 将 $[i,j]$ 拆成两个偶数段,相加取最小值;
- 将 $[i+1,j-1]$ 拆成两个偶数段,相加,并加上 $|a_i-a_j|$ 取最小值。
如上转移即可。时间复杂度 $\mathcal{O}(n^3)$。
代码:https://atcoder.jp/contests/typical90/submissions/37984053
020 - Log Inequality(★3)
题意
比较 $\log_2 a$ 与 $b \log_2 c$ 的大小。
$1 \le a \le 9 \times 10^{18}$,$1 \le b \le 17$,$1 \le c \le 13$。
解析
两边变指数,分别变为 $a$ 与 $c^b$ 比大小。
代码:https://atcoder.jp/contests/typical90/submissions/38166870
025 - Digit Product Equation(★7)
题意
定义 $f(x)$ 表示 $x$ 十进制下每位数字的乘积(不含前导 $0$)。
求 $1 \sim N$ 有多少数 $x$ 满足 $x-f(x)=B$。
$1 \le N,B \le 10^{11}$。
解析
先枚举当前有几个数位。对于每个数位 DFS 一次。
看到这有人会问:这范围那么大,DFS 不会搜爆掉吗?OK,我们来分析一下怎么搜。
没有剪枝的时候,每个数位都有 $10$ 种可能,但实际上这会产生很大的效率浪费。
比如 $123,132,213,231,312,321$ 这六个数,它们的 $f(x)$ 均为 $6$。
然后 $B$ 是固定的,可以知道此时 $B+f(x)$ 已经被确定下来了。所以这 $6$ 个数最多只会有一个是符合条件的。
这个事情告诉我们,我们在搜索数字的时候,可以强制后面的数位大于等于前面的数位。
之后,我们需要判断,将当前的 $x$ 数位改变一些顺序,能不能使得它等于定值 $f(x)+B$。
此时不需要大费周章去全排列枚举,只需要将 $x$ 和 $f(x)+B$ 两数的数位分别升序排列,比较这两个序列相同情况就可以了。
注意判断数字是否在上界 $N$ 以内。
这个做法时间复杂度并不好计算,但是可以写个程序测试一下,可以发现这种方式生成的 $x$ 不到 $5 \times 10^5$ 个。
代码:https://atcoder.jp/contests/typical90/submissions/37100318
029 - Long Bricks(★5)
题意
一条直线按顺序有 $W$ 个位置,接下来天上会掉下来 $n$ 个横条形的俄罗斯方块。
第 $i$ 个方块占据了位置 $[L_i,R_i]$。根据俄罗斯方块规则,它会一直下落直到底面的某处碰到了地面或别的方块。
如果每个俄罗斯方块高为 $1$,请求出每个方块停止下落的时候,它所在的高度。
$1 \le L_i \le R_i \le W \le 5 \times 10^5$,$1 \le N \le 2.5 \times 10^5$。
解析
本题涉及到区间求 $\max$,区间修改,是个经典的线段树维护懒标记的题目。
由于这题比较模板,就不多说了,具体细节可以看代码:https://atcoder.jp/contests/typical90/submissions/38139303
030 - K Factors(★5)
题意
给定 $N,K$,$1 \sim N$ 有多少个正整数,它至少有 $K$ 个素因数?
$2 \le N \le 10^7$,$K \le 8$。
解析
两个做法:
- 埃氏筛法,每个质数向后跳倍数,时间复杂度 $\mathcal{O}(n \log \log n)$。
- 线性筛。记 $fac(i)$ 表示数字 $i$ 的最大素因子,设数字 $x$ 的质因子数为 $f(x)$,那么可以 $\mathcal{O}(n)$ 转移,如下:
$$f(x)=f(\frac{x}{fac(x)})+[fac(\frac{x}{fac(x)}) \neq fac(x)]$$
代码(第一种做法):https://atcoder.jp/contests/typical90/submissions/38139369
039 - Tree Distance(★5)
题意
给定一棵 $n$ 个节点的树,树上的边边权均为 $1$,求树上两两节点距离之和。
$2 \le n \le 10^5$。
解析
这是一个经典的树上换根 DP 题。下面简述下解法:
- 第一步,用一次 DFS 遍历算出以 $1$ 号节点为根,到其它每个点的距离 $d(i)$,以及每个节点以自己为根的子树大小 $size(i)$。
- 设此时的距离和为 $S$。
- 第二步,换根。同样按照 DFS 遍历顺序,如果以前根为 $x$,此时根从 $x$ 换成 $y$。
- 对于$S$ 此时将变成 $S-size(y)+n-size(y)$。因为就 $y$ 及其子树而言,它到新根的距离都减少了 $1$;就子树 $y$ 以外的节点而言,它到新根的距离都增加了 $1$。
- 将每个点为根时的 $S$ 相加,就是答案。
注意 DFS 回溯时要还原信息。代码:https://atcoder.jp/contests/typical90/submissions/38139648
066 - Various Arrays(★5)
题意
见 YZOJ 5973。
有一个长度为 $n$ 的序列 $a_1,a_2,...,a_n$。其中 $a_i$ 将从 $[L_i,R_i]$ 区间中随机抽取一个整数作为它的值。
请求出整个序列的逆序对的期望数。
原题范围:$1 \le n \le 100$,$1 \le L_i \le R_i \le 100$。
加强版范围:$1 \le n \le 5000$,$R_i$ 在保证存在 double 内精度正常的情况下无特殊限制。
解析
由期望的线性性,整个逆序对的期望数,等于两两位置比较,产生的逆序对的期望数。
而所谓两两位置比较的逆序对,就是位置靠前的数大于位置靠后的数的概率!
所以先两两枚举下标 $i,j \ (i \le j)$,接着 $a_i,a_j$ 也逐个枚举,判断它们的大小,累加后除以总数即得到概率。
时间复杂度 $\mathcal{O}(n^4)$ 级别($n$ 与 $a_i$ 同阶),通过前缀和优化可以达到 $\mathcal{O}(n^3)$。
对于加强版,我们就不能逐个枚举 $a_i,a_j$ 的值了,而是直接根据这两组 $[L_i,R_i], \ \ [L_j,R_j]$ 比较 $a_i>a_j$ 的概率。
此时要分六类讨论:
- $R_i < L_j$,易知概率为 $0$;
- $L_i > R_j$,易知概率为 $1$;
- $L_i \le L_j \le R_i \le R_j$,分开考虑区间 $[L_i,R_i]$ 与 $[L_j,R_j]$ 的相交片段的概率,和分离片段的概率。
- $L_j \le L_i \le R_j \le R_i$,同上一种情况。
- $L_j \le L_i \le R_i \le R_j$($i$ 是 $j$ 的子区间);
- $L_i \le L_j \le R_j \le R_i$($j$ 是 $i$ 的子区间)。
此时时间复杂度 $\mathcal{O}(n^2)$。代码:https://atcoder.jp/contests/typical90/submissions/38141000