DS 乱做
CF1422F Boring Queries
给定一个长度为 \(n\) 的序列 \(a\) 以及 \(q\) 次询问。
每次询问包含 \(2\) 个整数 \(l,r\) ,你需要求出区间 \([l,r]\) 的最小公倍数对 \(10^9 + 7\) 取模的结果。
\(1\leq n,q\leq 10^5,1 \leq a_i\leq 2\cdot 10^5,1\leq x,y \leq 10^5\)。
这题有两个做法,首先讲 polylog 的,这基本上算一个套路了。
考虑最小公倍数直接求一定是困难的,我们套路地把这个东西放到质数幂上讨论。
因为质数幂需要取 \(\max\),一般直接维护是困难的,考虑如果离线,我们可以直接扫描线。如何维护该右端点的信息呢?我们发现可以记录上一个 \(p^k\) 出现的位置,然后在上一个位置乘上 \(p^{-1}\),因为这里我们已经贡献了 \(p\) 这个因子了。这样我们用一个主席树预处理就行了。时间复杂度 2log。
这题还有一个根号分治做法,考虑一个经典的事情,就是有用的质因子级别只有 \(O(\sqrt V)\) 的,因为大于这个值的质因子最多只有一个,这道题中可以直接单独用一个主席树维护。剩下的所有质数就用一个取 \(\max\) 的线段树维护即可。时间复杂度为 \(O(n\log n\log V+q\sqrt V)\)。后面除以 \(\ln\) 和乘上一个 \(\log\) 抵消了。当然你也可以 ST 表。
CF811E Vladik and Entertaining Flags
在一个 \(n*m\) 的网格上每个格子都有颜色,\(q\) 次询问,每次询问只保留 \(l\) 至 \(r\) 列时有多少个四连通的颜色块。两个格子同色但不连通算在不同的颜色块内。
\(1\leq n\leq 10,1\leq m,q\leq 10^5\)。
先讲个笑话,这个题 EI 的 \(O(nm+q)\) 没跑过另外一个人的小常数 \(O(nm\alpha(m)+qn)\),获得 CF 上 rk2 好成绩。
这个题 4 个复杂度,这里不讲 \(O(nm+q)\),因为过于复杂。
首先是最容易的 \(O(nm\alpha(n)+qn\log m\alpha(n))\)。
直接用线段树维护,考虑怎么合并两个区间,直接记录两端并查集即可。合并和撤销就只撤销涉及到的点的 \(fa\) 即可。这样我们能知道合并在一起的连通块有多少个,就可以做一个减法得到这个区间的连通块个数。我的实现跑了 1100ms+,非常慢。
然后就是一个靠结论比较容易想到的 \(O((nm+q)\log n)\)。
首先有平面图欧拉公式:
其中 \(V\) 是点数,\(E\) 是边数,\(F\) 是被分割出来多少个不相连的区域数,\(C\) 是连通块个数。我们把颜色相同的格子连接起来,问题就变成了求 \(C\)。而 \(V,E\) 是容易处理的,一个直接算,一个前缀和。现在问题变到了如何求 \(F\) 上。
考虑 BFS 找到所有面的最左侧和最右侧需要的格子,现在就变成了有多少 \((l,r)\) 满足 \(L\leq l\leq r\leq R\),其中 \(L,R\) 为询问的区间,这就是一个二维偏序,离线树状数组即可。我的实现跑了 296ms,在 CF 上跑到了第一页。
这时候我打开了第一的程序,然后发现其实我们大可不用这样二维偏序。因为这个题 \(n\leq 10\),我们可以得到一个理论 \(O(nm+qn)\) 的做法,把上一个做法的 \(\log\) 平衡掉了。第一使用了并查集,而我使用的是 DFS,其实差不多,我 DFS 也带一个 \(4\) 倍常数。
考虑对于一列,最多有 \(2n\) 个区域(其中如果每个区域不记左端点,那么只有至多 \(n\) 个区域)穿过它,所以我们大可以统计右端点在这里的区域个数,然后对其做一个前缀和。这样我们多算的就是穿过了 \(l-1\) 和 \(l\) 的区域,我们可以直接对于每个区域都把自己 push 到每一列的桶里。易得桶的大小 \(\leq 2n\)(或 \(n\)),暴力枚举 \(l\) 处的桶减去多余贡献就行了。但是实测我跑得巨慢,跑了 421ms/tuu。经过一堆卡常之后最快也只有 296ms,真傻比。
CF1946F Nobody is needed
一个长度为 \(n\) 的排列 \(a_{1...n}\)。有 \(q\) 次询问,每次给出 \(l,r\),求有多少个子序列 \(i_{1...k}\) 满足:
\(i_1\ge l,\space i_k\le r\)
\(\forall j\in [1,k-1],a_{i_j}|a_{i_{j+1}}\)
多测,\(T\) 组数据。
\(\sum n,\sum q\le 10^6\)。
duel 的时候没做出来。
考虑扫描线 DP。设 \(f_i\) 为 \(i\) 开始的合法子序列个数。显然加入一个数只会对在前面的其因数产生影响。那么一种想法就是把前面的其因数拉出来重新 DP 得到各处的贡献。
不过有一个简易的想法是,这个等同于可以在每个其因数开始的部分子序列在后面接上一个该数,考虑对于每一个位置计算从前面的因数到这个数的合法子序列个数,这样转移的时候只需要枚举 \(k|j|i\),然后把所有 \(k\to j\) 的贡献累加到 \(k\to i\) 上面。使用树状数组维护 \(f\) 的变化,这样做是 \(O(n\log^2 n+q\log n)\) 的,非常好写。但是不知道为什么跑得很慢,就算把 vector 换成指针。