2021年第44周总结
11.1
判断线段相交问题
解决计算几何中判断两个直线相交问题,通过矢量叉乘判断向量的相对位置,叉乘也可判断凸包等。
CF 751
Problem D
此题属于BFS中边数较多的情况,尝试采用删点的方法降低时间复杂度,删除后某些点将不会被搜索到。
可以使用Set维护待搜索的点集合。
思路&难点:Set优化的BFS。
LC No.265
Probelm D
编辑距离超级加强版。
一开始考虑 d p [ i ] [ j ] dp[i][j] dp[i][j]为布尔型是否匹配,后来发现其具有后效性,因此该方法不行,DP在设计状态的时候首先考虑是否有无后效性。
新的DP方法,通过前推少量状态而不是枚举状态减少常数。
定义 d p [ i ] [ j ] dp[i][j] dp[i][j]为匹配到 s 1 [ i ] s1[i] s1[i]和 s 2 [ j ] s2[j] s2[j]的不产生冲突可能的剩余差值(由通配符产生)。
通过枚举 d p [ i ] [ j ] dp[i][j] dp[i][j]的状态去推出后继的状态。
其分为两种情况:
- 追加差值
- 匹配差值
分类讨论即可。
思路:二维DP。
难点:选择合适的DP状态解决后效性。
11.2
CF 751
Problem E
可以看做是模板题。给定两个数组, a a a的相对位置不变, b b b以任意顺序插入到 a a a中,求最小逆序对。
首先需要证明两个结论:
- b b b一定以从小到大的相对顺序进行插入,证明显然。
- b i b_i bi的插入的最优性互相独立互不影响。
第二点的证明,假设 b i b_i bi在 a j a_j aj的前面插入对逆序对的贡献是最少的,那么我们知道, a j > b i a_j>b_i aj>bi若 b i b_i bi插在 a j a_j aj后面,则会多产生一个逆序对,并且小于 b i b_i bi的 b b b也不会产生更大的贡献。
首先的一个思路从寻找 b 1 b_1 b1的最优插入位置 p 1 p_1 p1,这需要 O ( n ) O(n) O(n)的时间,在 p 1 p_1 p1之后查找 p 2 p_2 p2的位置,这需要 O ( n ) O(n) O(n)时间,总共需要 O ( n m ) O(nm) O(nm)的时间。如果我们使用分治,每次查找 b m i d b_{mid} bmid的最优位置,每一层需要 O ( n ) O(n) O(n)的时间,总共需要 O ( n log m ) O(n\log m) O(nlogm)。
最后树状数组统计逆序对的个数,然后统计即可,时间复杂度 O ( ( n + m ) log ( n + m ) ) O((n+m)\log (n + m)) O((n+m)log(n+m))。
思路:贪心证明+分治。
难点:分成子问题优先考虑分治。
CF 752
Problem E
结论题 + 贡献转移。
考虑简化这个过程,能够得到一些有用的结论,即若 a i + 1 < a i a_{i+1}<a_i ai+1<ai,那么最终 a i = ⌊ a ⌈ a i a i + 1 ⌉ ⌋ a_i=\lfloor \frac{a}{\lceil \frac{a_i}{a_i + 1} \rceil} \rfloor ai=⌊⌈ai+1ai⌉a⌋,并且需要 ⌈ a i a i + 1 ⌉ − 1 \lceil \frac{a_i}{a_i + 1} \rceil - 1 ⌈ai+1ai⌉−1次拆解。
那么我们可以枚举以 x x x结尾,前驱为 a i + 1 a_i + 1 ai+1的 ⌈ a i a i + 1 ⌉ − 1 \lceil \frac{a_i}{a_i + 1} \rceil - 1 ⌈ai+1ai⌉−1贡献。设 d p [ i ] [ x ] dp[i][x] dp[i][x]表示为子数组 a [ i : j ] a[i:j] a[i:j]最终以 x x x结尾的子数组的数量,那么前面有 i i i个开头的子数组会得到这个贡献。即为 i ∗ d p [ i ] [ x ] ∗ ( ⌈ a i x ⌉ − 1 ) i * dp[i][x] * (\lceil \frac{a_i}{x} \rceil - 1) i∗dp[i][x]∗(⌈xai⌉−1)。
思路:推结论+贡献转移。
难点:推出结论+ DP状态设计 + 贡献转移技巧。
11.3
LC 407
优先队列的优化的BFS搜索题。
我们假设一个小人在 ( r , c ) (r,c) (r,c)向四周倒水,优先队列中有三元组 ( r , c , h e i ) (r,c,hei) (r,c,hei),表示小人在 ( r , c ) (r,c) (r,c)向四周倒水的最大高度为 h e i hei hei。
首先我们可以确定一开始小人应该站在边界处,故将所有的边界加入到队列中。
每次队列取出 h e i hei hei最小的,向四周倒水,如果 ( d r , d c ) (dr,dc) (dr,dc)的高度低于 h e i hei hei,那么肯定是可以倒水的高度为 h e i hei hei的,因为 ( d r , d c ) (dr,dc) (dr,dc)的短板一定是 ( r , c ) (r,c) (r,c)。然后 ( d r , d c ) (dr,dc) (dr,dc)的 h e i hei hei就可以确定,即 max ( h e i , h e i g h t [ d r ] [ d c ] ) \max(hei,height[dr][dc]) max(hei,height[dr][dc]),将其加入优先队列中。
思路&难点:优先队列维护的BFS。
11.5
21 CCPC for Female
Problem C
一开始ych告诉我将分公司的数量为 1 1 1和大于 1 1 1进行分类,为 1 1 1的就不要赋予状态。
正解为传递闭包优化DAG上的DP。
考虑带中间节点的路径 i → j i \to j i→j,那么若有边 ( i , j ) (i,j) (i,j)存在,那么以这条边转移的DP一定是不优的,所以可以删掉这个边。
为了维护节点 i j ij ij之间是否有带中间节点的路径,需要求一次DAG的传递闭包。
这样操作之后,可以证明带有 n n n个节点的DAG图最多有 n − 1 n-1 n−1个边。即 1 → n 1 \to n 1→n的一条链。
这样时间复杂度就从 O ( n 2 ) O(n^2) O(n2)的完全图优化为 O ( n ) O(n) O(n)的链图。
思路:DAG上的DP。
难点:用传递闭包优化DAG的DP。
Problem B
倍增,一个很显然的思路,在字符串 s s s上查询,从 s [ 0 ] s[0] s[0]到 s [ k 1 ] s[k_1] s[k1]正好凑齐一组字母表,从 s [ k 1 + 1 ] s[k_1 + 1] s[k1+1]到 s [ k 2 ] s[k_2] s[k2]正好凑齐一组字母表,结果正好能最多凑齐 p p p个字母表,那么关于 s s s的答案即为 p + 1 p+1 p+1。
考虑 d p [ i ] [ k ] dp[i][k] dp[i][k]为从起点 i i i跳 2 k 2^k 2k次的中点。首先通过一次二分计算出 d p [ i ] [ 0 ] dp[i][0] dp[i][0],然后进行倍增求出所有的 d p [ i ] [ k ] dp[i][k] dp[i][k]即可。
单次查询时的时间复杂度为 O ( log n ) O(\log n) O(logn)。
思路&难点:二分+倍增DP。
11.6
21 CCPC for Female
Problem F
POJ 2185的增强版本 广义KMP&二维KMP的模板题,只不过需要一次Hash来优化字符串的判断相等。
思路&难点:二维KMP+Hash优化。
11.8
ABC 226
Problem F
一道分拆数估计+DFS枚举分拆+第一类斯特林数+划分模型的一道综合计数题。
首先根据 P 50 < 1 e 6 P_{50} < 1e6 P50<1e6,确定DFS枚举分拆可行。
然后DFS枚举分拆,在可能的答案中对分拆组转换为划分组,最后根据公式 S ( n , 1 ) = ( n − 1 ) ! S(n,1) = (n - 1)! S(n,1)=(n−1)!对其进行计数。
思路:组合数学。
难点:计算公式的确定。