2023.5 codeforces 杂题选做
E. Location
\(n\le 5\times 10^4\) 的范围,并且区间赋值、区间维护最值,可以考虑分块。然后对于散块的修改可以直接暴力,对于整块,由于 \(b_i\) 值不变,\(a_i\) 值只有一个,思考如何快速求。
由于 \(\dfrac{\operatorname{lcm}(a_i,b_i)}{\gcd(a_i,b_i)}=\dfrac{a_ib_i}{\gcd^2(a_i, b_i)}\),然后这种式子一般考虑枚举 \(\gcd\) 的值 \(k\),然后求这个式子的最小值。我最开始想成维护这个式子的最大值,导致没做出来,其实维护最小值的话一般能想到维护 \(\min\{b_i|\gcd(a_i,b_i)=k\}\),但这个不好维护,而 \(\min\{b_i|b_i\bmod k=0\}\) 比较好维护,观察又能发现如果此时 \(\gcd(a_i,b_i)\ge k\) 的话用 \(\dfrac{a_ib_i}{k^2}\) 更新答案是不会使答案出现错误的,所以说直接这样维护就行,用一下 \(\mathcal O(1)\) 求 \(\gcd\),总复杂度 \(\mathcal O(n\sqrt n\ d(n))\)。
G. Delicious Dessert
这题我最开始想的 SA,发现不是很好维护,其实 SAM 特别好维护,只要维护一下 \(parent\ tree\) 的子树 \(endpos\) 大小即可,然后统计一下答案。
F. Bracket Insertion
咕咕咕
D. Edge Elimination
构造比较好说,从下到上考虑,能删就删,不能就删父亲向上连的边,然后接着删,就行了,实现的话可以记录 \(cnt_{x,0/1}\) 表示跟点 \(x\) 相邻的边有多少条在奇数或者偶数时删掉,要满足 \(cnt_{x,0}=\lfloor\dfrac{cnt_{x,0}+cnt_{x,1}}{2}\rfloor\) 才能构造,然后判断完可行性之后按照顺序模拟删边即可。
D. The way home
由于 \(n\le 800\),并且感觉是一个神秘的最短路优化 DP,所以先跑一边 Floyd。接着观察可以发现,我们演出的地方的演出报酬一定是单调递增的,并且从我们在 \(j\) 号点演出之后去 \(i\) 号点的话费用永远是恰好够,即到达 \(i\) 号点之后剩余的钱数 \(0\le rest<w_j\)。因此我们的 DP 要记录两个状态——刚刚到达 \(i\) 号点已经演出的次数和剩余钱数。这个二元组的比较我当时没想到如何贪心,其实比较的时候两个二元组只要演出次数不同就选择演出次数小的即可,否则选择剩余钱数大的,证明如下:
记从 \(j\) 转移到 \(i\) 的二元组是 \((a,b)\),从 \(k\) 到 \(i\) 的二元组是 \((x,y)\),\(x-a\ge 1,b<y\),那么由于 \(w_j<w_i,w_k<w_i,b<w_j,y<w_k\),所以 \((x-a)w_i+b>y\),故 \((a,b)\) 更优,而其他情况比较好证明就不在这里说明了,所以上述比较方法是正确的。
E. Gasoline prices
由于限制是保留的,具有传递性的,所以是一个类似于并查集的东西,也就是说有效的修改只有 \(\mathcal O(n)\) 次,所以说现在的问题就转化成了如何快速找到两个不同的并查集并且将他们并在一起。
因为并查集维护答案比较容易,所以这里就不说如何维护信息了。这里说一下如何找到不同元素,也就是找到 \([a,b]\) 和 \([c,d]\) 中对应点的 \(col\) 不同的点对。这里先处理一个小问题,就是如何修改 \(col\),可以启发式合并。然后对于找不同的 \(col\) 其实我最开始想的是线段树二分,为了找到最靠左的 \([a,b]\) 和 \([c,d]\) 中不同的 \(col\),但其实可以对每一段路径的颜色的排列进行 Hash,这里注意一下 \([a,b]\) 和 \([b,a]\) 哈希值不同。这个 Hash 方法先对每种 \(col\) 随机出来一个权值,然后维护一个按进制 Hash,具体的可以维护根到每个点 \(x\) 路径的进制 Hash 值和 \(x\) 到根的进制 Hash 值,然后如果我们要求路径 \([x,y]\) 的 Hash 值,就找到 \([x,lca]\) 的 Hash 值再和 \((lca,y]\) 的 Hash 值合并,方法跟字符串 Hash 方法一样,但其实还有一个问题:修改 \(col\) 之后 Hash 值也会变,这个值如果快速更改,显然可以树状数组维护。
总结一下,找到一种维护树上路径 Hash 的方法,二分找到第一个 \(col\) 不同的位置,然后启发式合并维护并查集,树状数组修改 Hash 值,总复杂度 \(\mathcal O(n\log^2 n)\),即每一次有效的修改需要二分位置、树状数组修改还需要一个 \(\log n\),共 \(\log^2n\)。
F. Another n-dimensional chocolate bar
形式化题意:给定长度为 \(n\) 的数列 \(\{a_i\}\) 和一个数 \(k\),请找到一个长度为 \(n\) 的数列 \(\{b_i\}\) 使得 \(\prod b_i\ge k\),并且令 \(k\prod_{i=1}^{n} \lfloor\dfrac{a_i}{b_i}\rfloor\dfrac{1}{a_i}\) 最大,求出这个最大值。
先把常数提出来,那么剩下就是求 \(\prod\lfloor\dfrac{a_i}{b_i}\rfloor\) 的最大值,一个比较朴素的 DP 是 \(dp_{i,j}\) 表示考虑到前 \(i\) 个数,目前前缀乘积为 \(j\) 的最大价值,但显然这样是 \(\mathcal O(nk^2)\) 的,我没想到咋优化,但只要改一下状态就能更好,即求 \(dp_{i,j}\) 表示考虑了前 \(i\) 个数,后 \(n-i\) 个数成绩至少为 \(j\) 时的最大价值,这样有什么好处呢?因为这样转移是:
\[dp_{i,\lceil\frac{j}{t}\rceil}=dp_{i,j}\times\lfloor\dfrac{a_i}{t}\rfloor \]所以说第二维是 \(k\) 的数论分块个数,即 \(\mathcal O(\sqrt k)\),那么复杂度变为了 \(\mathcal O(nk)\),但其实还能进一步优化,因为 \(t\) 的取值其实影响种类数也是数论分块个数,为 \(j\) 的数论分块,所以最后就是两层数量分块套起来,复杂度 \(\mathcal O(nk^{\frac{3}{4}})\)。
D. Cute number
观察一下可以发现一些比较有用的性质:合法的区间为 \([i^2,i(i+1)],i\in[1,+\infty)\),不合法区间为 \([i(i+1)+1,(i+1)^2-1],i\in[1,+\infty)\),所以合法不合法区间交替出现,并且区间长度单调不降,所以说 \(a_1\) 所在的合法区间 \([i^2,i(i+1)]\) 一定满足 \(i\le a_n-a_1\),而这种题直接找最小值显然比较困难,所以说其实可以尝试一下枚举 \(a_1\) 所在的位置然后去 check。
设当前 \(a_1\) 在第 \(i\) 个合法区间,那么容易发现如果 \(a_{j+1}-a_j\le i\) 就有 \(a_{j}\) 和 \(a_{j+1}\) 一定在同一个区间,所以说如果把所有 \(a_{j+1}-a_j<i\) 的数合并成一段之后序列就变成了若干个段,而段的个数 \(num\) 一定是小于 \(\dfrac{a_n-a_1}{k}\),因为每两段之间的间隔大于 \(i\),所以如果有超过 \(\dfrac{a_n-a_1}{k}\),则就有 \(mx-mn>a_n-a_1\),显然不合法,所以只要能在 \(\mathcal O(num)\) 的时间 check 即可。
分析可知,每一段一定是移动到大于它左端点的第一个合法段,所以 check 方法就是算一下所有段左端点为合法最小的 \(k\) 记为 \(p\),以及右端点合法的最大的 \(k\) 记为 \(q\),然后只有 \(p\le q\) 才合法。
然后就结束了,复杂度 \(\mathcal O(n\log n)\)。
G. Autocompletion
显然这种题看起来就很 AC 自动机,虽然我先想了一会儿SAM。当然准确的说这题只需要用到字典树,然后考虑进行 DP,设 \(dp_i\) 表示到达字典树上第 \(i\) 个节点所需要的最小代价。第一种操作的转移很显然是 \(dp_i=\min_{j\in anc_i} dp_j+depth_i-depth_j\),而对于第二种操作,对于从 \(j\) 到 \(i\) 转移,其实代价本质是 \(j\) 子树内比 \(i\) 号节点 DFS 序小的关键节点数,而如果我们从按照 \(\texttt{a}\sim \texttt{z}\) 的顺序去遍历出边,那么代价就是经过 \(j\) 节点之后、到达 \(i\) 节点之前经过的关键节点数,那么如果我们记 \(num_{i,0/1}\) 表示到达 \(i\) 号节点之前(\(num_{i,0}\) 表示不包括 \(i\) 号节点,\(num_{i,1}\) 表示包括)经过的关键节点数,则第二种方式转移为 \(dp_i=\min_{j\in anc_i}dp_j+num_{i,1}-num_{j,0},s_i\in S\),然后考虑优化,可以用一个栈维护 \(anc\),然后分别维护栈内 \(\min\{dp_j-depth_j\}\) 和 \(\min\{dp_j-num_{j,0}\}\) 即可,复杂度 \(\mathcal O(n)\)。
B. Pave the Parallelepiped
计数题,很容易算重,所以考虑 DP。设 \(S_i\) 表示 \(A,B,C\) 中能被 \(i\) 整除的数的集合(即是一个三位二进制数),然后可以发现三元组 \((a,b,c)\) 合法的充要条件是(以下各项条件都要满足):
- \(S_a\bigcup S_b\bigcup S_c={A,B,C}\)
- \(S_a\not=S_b\or |S_a|\not=1\)
- \(S_b\not=S_c\or |S_b|\not=1\)
- \(S_c\not=S_a\or |S_c|\not=1\)
其实 2,3,4 用人话说就是每种大小为 \(1\) 的集合只能选一种,然后考虑枚举选哪种然后剩下的组合数乱算就行了。
C. Hyperregular Bracket Strings
首先由于如果给定的两个区间 \([l_1,r_1]\) 和 \([l_2,r_2]\) 相交,假设 \(l_1\le l_2\),那么一定有 \([l_1,l_2-1],[l_2,r_1],[r_1+1,r_2]\) 这三个区间都分别合法,那么归纳一下可以发现:
记 \(S_i\) 为将 \(i\) 这个位置覆盖到的线段的集合,\(T_i\) 表示所有 \(S_j=i\) 的 \(j\) 的集合,那么就有所有 \(j\in T_i\) 组成的子序列是一个合法的括号序列
因此对于每种线段随机一个 Hash 值,把每种 \(S\) 集合 Hash 表示出来,然后把所有 \(T\) 集合的大小算出来,然后 \(\text{Catalan}\) 数算即可。
D. Mex Tree
树形 DP。设 \(dp_i\) 表示只考虑 \(i\) 子树内的路径的最大贡献,由于还要计算跨子树的贡献,所以需要记录更多状态,而我们只需要额外考虑跨子树,所以说设 \(dp_{i,j,0/1}\) 表示 \(i\) 选 \(0/1\),\(i\) 子树内与 \(i\) 同色的包括点 \(i\) 的极大连通块大小为 \(j\) 时的最大贡献,然后就可以转移了。
考虑优化,因为我们黑白染色之后答案是 \(\dfrac{n(n-1)}{2}+\mathcal O(n)\) 级别,而大小为 \(j\) 的同色连通块会降低 \(\mathcal O(j^2)\) 的贡献,因此 \(j\le \sqrt n\),然后就复杂度 \(\mathcal O(n\sqrt n)\)。
标签:Hash,dfrac,codeforces,然后,2023.5,mathcal,维护,杂题,dp From: https://www.cnblogs.com/zyc070419-blog/p/17455465.html