转存的 2025 赛季训练
这里是 2024 - 2025 赛季的训练。
P2519
妙。考虑转化条件。对于一个 \(a_i,b_i\),转化成分数从大到小排序后编号在 \([a_i+1,n-b_i]\) 的人的分数相等。
那么我们可以得到若干个 \([l,r]\),在这些 \([l,r]\) 中取尽量多的,互不相交的即可。dp 解决。
P2516
被打爆了。首先把 \(f_{i,j}\) 表示 \(A_{1\sim i}\) 和 \(B_{1,j}\) 的 LCS 跑出来。然后记 \(g_{i,j}\) 为方案数。考虑转移:
- \(f_{i,j} = f_{i-1,j}\)。此时 \(A_i\) 不在 LCS 中。\(g_{i,j} = g_{i-1,j}\)
- \(f_{i,j} = f_{i,j-1}\)。此时 \(B_j\) 不在 LCS 中。\(g_{i,j} = g_{i,j-1}\)
- \(f_{i,j} = f_{i-1,j-1}\)。此时 \(A_i,B_j\) 在 LCS 中。\(g_{i,j} = g_{i-1,j-1}\)
上述相加即可。但是考虑 \(A_i \ne B_j\) 且 \(f_{i-1,j}=f_{i,j-1}=f_{i-1,j-1}\) 的情况,此时 \(g_{i-1,j-1}\) 被算了两次。减去即可。
注意要滚动数组。
P2051
喜欢给极小的样例还不给大样例的出题人你们好啊。
显然每行、列的炮至多有 \(2\) 个。
50pts 可以考虑状压,这是典中典了。
考虑把炮的个数压入状态。\(f_{i,j,k}\) 表示前 \(i\) 行,\(j\) 列放了一个炮,\(k\) 列放了两个炮。
分讨第 \(i\) 行放了 \(0/1/2\) 个炮讨论即可。
CF149D
好区间 dp。
显然的区间 dp。首先由于保证是一个合法的括号序列,这启发我们 dp 时每次分割成两个合法区间,再进行 dp。
那么每次可以分割成两个合法的区间。当 \((l,r)\) 配对时,特殊处理一下方案数。
使用记搜实现。
P10680
st 表好题。
首先注意到这个神秘的 \([l,l+2^k-1]\) 以及这个神秘的合并操作,启发我们使用 st 表。原因是 st 表可以高效合并两个一半区间。
不带修就是 st 表板子。带修捏?
首先要明确的是 st 表单点修改一次重构是 \(\mathcal{O(n)}\) 的。具体是因为单点影响的范围极小。
考虑阈值分治。对于 \(st_{i,k}\) 的 \(k\) 较小的部分,暴力重构。在查询时,暴力从两个子区间合并即可。
可以证明阈值 \(B = 10\),时间复杂度 \(\mathcal{O(n\sqrt n)}\)。具体不会证。
P10334
小清新贪心。
先把无解判掉。
正着做不太好做,考虑倒着枚举时间。
显然对于某个时刻,它的一些需求可能需要在前面的时刻制作。
我们维护一个栈来存放当前的需求。假设对于当前这个 \(t_i\),后面还有一些需求 \(a_x\) 没有完成,那么当前这个位置的需求 \(a_i\) 需要 \(\ge \max\{a_x\}\)。否则第 \(i\) 个人来就不会取 \(a_i\) 而是 \(\max\{a_x\}\)(\(a_x\) 是要留给后面的,必然存在),后面你再去制作 \(a_x\) 的话代价就更大了。这一部分需要好好理解。
那么对于某个 \(t_i\),它的需求就是 \(\max(A_i, \max\{A_x\})\)。后者可以用栈来维护。
那么对于我们现在的 \(t_i\),共有 \(t_{i+1} - t_i\) 个时刻给我们完成后面的需求。每次取出栈顶即可。原因是栈中尽量大的数要尽量早出来,不然它留在栈顶在前面会影响更多 \(A\)。感性理解一下。
那么做完啦。很多人认为这是单调栈。但笔者认为这只是具有单调性质的栈罢了。时间复杂度线性。
ARC201C
P1600
好但是唐题啊。
前 60pts 很唐。
考虑所有 \(t_i = 1\) 的情况。实际上不难想,难点在于维护。有个 trick 由于我们只关心某一项的值。子树内的答案是更新后的减去原来的。
考虑正解。记 \(dep_x\) 表示根节点到 \(x\) 的边数。对于一条路径 \((s,t)\),将其拆分为 \((s,lca)\) 与 \((lca,t)\)。那么 \((s,lca)\) 上某个点 \(u\) 若有贡献当且仅当 \(dep_s = dep_u + W_u\)。同理 \((lca,t)\) 上点 \(u\) 有贡献当且仅当 \(dep_{lca} \times 2 + dep_s = dep_u - W_u\)。
观察到等式左边为定值。那么对于每个 \(u\),计算它子树内有多少条路径满足即可。具体可以树上差分。然后动态维护当前有多少 \(dep_s = W_u\) 或 \(dep_{lca} \times 2 + dep_s = dep_u - W_u\)。这一部分是容易的。但是可能会算重。容易证明重复的只有根节点,也就是某条路径可能被根节点算两次。因此树上差分时只加一边就好了。
\(\mathcal{O(n\log n)}\)。
P4170
怎么区间 dp 这么难。
考虑区间 dp。\(F_{i,j}\) 表示 \([i,j]\) 已满足条件的最小答案。
转移显然考虑枚举断点 \(k\),用 \(F_{i,k}+F_{k+1,j}\) 去更新。
当 \(S_i = S_j\) 时,显然可以找到前面某次对 \(i\) 染色的操作 \((i,x)\),考虑将 \(x \leftarrow j\),并将这次操作挪到最后,容易证明原序列不变且操作次数不变。所以可以用 \(F_{i,j-1}\) 来转移。
ABC424D
ABC424E
弘文了。
考虑朴素暴力。考虑优化。注意到我们对同一元素进行了太多重复操作。于是把元素的个数同时压进优先队列。那么做完了。由于一个数的分裂会很快。
注意不能清空堆。空间换时间,每次新开一个。
ABC424F
天天犯唐。
线段树的做法有些难想。考虑哈希。每次把 \([A,B]\) 随机一个权值,把 \([A,B]\) 区间异或上这个权值。那么两个点的权值相同当且仅当覆盖它们的线段集合相同,那么每次单点判断即可。
区间异或、单点修改。线段树随便维护。
P1272
树形 dp 怎么这么难。
明确一下状态:\(F_{i,j,k}\) 表示以 \(i\) 为根,且此时 \(i\) 与父节点相连,考虑 \(i\) 的前 \(k\) 个儿子在子树内得到 \(j\) 个点需割掉的最少的边。
考虑转移。考虑 \(u \to v\)。若 \(v\) 子树内不取,\(F_{u,j,k} = F_{u,j,k-1} + 1\)。即把其割掉。
否则枚举给 \(v\) 子树 \(s\) 个。\(F_{u,j,k} = \min\{F_{u,j - s, k-1} + F_{v,j_v,k-1}\}\)。显然 \(k\) 可以滚掉。
需要注意的是,我们需要强制选根。不然就不能把那些选的子树连接起来。因此对于非根节点。由于我们强制选根,那么需要把根和父亲这条边断开。因此答案再统计时要加 \(1\)。
P6033
遇到这种题,先考虑部分分。
朴素是带 log 的,考虑怎么做到线性。
我们用一个 蚯蚓 中的 trick。就是你合并之后一定是越来越大的,具有单调性。这个比蚯蚓那题的证明简单多了。
所以类似地,单独开一个数组来合并的结果,由于两边都是单调的每次只拿出队头作比较即可。
话说好像是这题的结论推广到蚯蚓那的吧?
P14072
热恋。
dp 好题。
假设我们将原排列分成两个大小为 \(n\) 的集合 \(A,B\)。由于 \(k \le 2n\),\(A\) 和 \(B\) 中必然有一个包含 \(k\),那么此时这个集合中数的乘积必然是 \(k\) 的倍数。也就是说,此时另一个集合中数的乘积也要求是 \(k\) 的倍数。
那么问题转化为,从原排列中选出 \(n\) 个数,要求不含 \(k\) 且其乘积为 \(k\) 的倍数。不妨从小到大选。记其方案数为 \(d\),那么答案就是 \(d \times 2 \times n! \times n!\)。这个是好理解的。
那么可以 dp 了。\(F_{i,j,l}\) 表示前 \(i\) 个数,选了 \(j\) 个,当前选出来的数之积对 \(k\) 取模为 \(l\) 的方案数。这样很好转移。分讨每个数选 or 不选。不选就是 \(F_{i,j,l} \leftarrow F_{i-1,j-1,l}\)。选的话就是 \(F_{i,j,(l \times i) \bmod k} \leftarrow F_{i,j,(l \times i) \bmod k} + F_{i-1,j-1,l}\)。那么做到 \(\mathcal{O(n^3)}\)。值得注意的是,一个数都没选时默认积为 \(1\),故边界为 \(F_{0,0,1} = 1\)。
咋优化捏?由于我们只关心是否是 \(k\) 的倍数,状态放余数有些浪费了。那么就记最大公约数!\(F_{i,j,l}\) 表示前 \(i\) 个数,选了 \(j\) 个,当前乘积与 \(k\) 的最大公约数为 \(l\) 的方案数。那么转移是类似的。但是有个难点,就是假设当前的最大公约数是 \(x\),怎么得到选上 \(i\) 后的最大公约数呢?这个手玩一下可以得到是 \(x \times \gcd(i,k/x)\)。还是比较好算的。那么预处理最大公约数,我们得到了 \(\mathcal{O(n^2\log n + n^2d(k))}\),其中 \(d(k)\) 表示 \(k\) 的因子个数。可以本地算一下 \(k = 2000\) 时 \(d(k) \le 64\)。得益于洛谷飞快的评测机,可以通过。
你会发现这样写出来会被卡一个点。这时需要卡卡常。把状态里第三维换为 \(k\) 的第 \(l\) 个因子即可。这样可以通过。
CF547B
手刃了。
考虑每个位置能影响到的两个端点。即对于 \(i\),左边最后一个不小于它的位置 \(L\),右边最后一个不小于它的位置 \(R\)。那么对于长度 \([1,R-L+1]\),\(i\) 都有贡献。
\(L,R\) 显然可以用单调栈维护。然后后面那个前缀最大值的话修改右端点就好了。差点爆了个 sgt 上去。
CF1407D
红温了。
显然的 dp。
先考虑第二种情况。这里假设考虑到 \(i\) 节点,正在寻找 \(j\) 去转移。注意到右边是 \(\min\),于是分讨 \(h_j,h_i\) 的大小关系。
- \(h_j < h_i\)。此时 \(\min = h_j\),也就是说,\(\max\{a_{j+1},\cdots c_{i-1}\} < h_j\)。那么维护一个自底到顶单调递减的栈,容易发现在用 \(i\) 维护栈时,弹出的恰好是满足条件的 \(j\) 节点。
- \(h_j \ge h_i\)。此时显然只有 \(i\) 前第一个 \(\ge h_i\) 的 \(h_j\) 可以被更新。
第三种情况与第二种情况类似。于是开两个栈解决问题。
但是要注意处理相同元素。具体地,以第二种情况为例,每次在弹出时,我们需要在保证弹出的这个位置 \(x\) 到 \(i\) 所有元素不相同,这样才可以从 \(x - 1\) 转移。
ABC352D
不会做小黄题。
考虑枚举连续的 \(k\) 个数。那么对于这 \(k\) 个数,答案就是数之中最大出现位置 - 最小出现位置。ds 维护即可。
CF1195E
先得把 P2216 过了。这两题一样的。就是 \(C_{i,j}\) 表示第 \(i\) 行,\([j,j+b-1]\) 中 \(A_{i,j}\) 的 \(\min\)。然后在 \(C\) 上以列做一遍就好了。
交上去 mle 还以为卡 deque。虚空调试 0.5h 发现删掉几个没用的数组就过了。唐完了。
P1080
怎么以前做的题现在不会了 /fn
显然的贪心。考虑对于数对 \((a_i,b_i),(a_j,b_j)\),那么如果交换后更劣的话,有 \(\max\{\frac{1}{b_i},\frac{a_i}{b_j}\ < \max\{\frac{1}{b_j},\frac{a_j}{b_i}\}\)。拆掉式子就变成 \(a_ib_i < a_jb_j\),排序即可。
CF1142B
好题啊。
由于是 \(A\) 的子序列,不要求连续。考虑每个 \(i\) 作为起点,每次往后跳,若能跳 \(n-1\) 次,则这个起点可行。
然后上述过程是可以倍增维护的。对于每个 \(i\),它跳完有一个最小位置 \(B_i\)。那么每个询问查询 \([L,R]\) 中 \(B_i\) 的 \(\min\) 即可,如果 \(\le R\) 就可行。
注意如果不能跳 \(B_i\) 要赋值为 \(m+1\)。
因为 UB 调了快 1h,我是奶龙。
CF1848F
Fun fact:正解是手模四次操作发现规律。做的时候手模三次没发现规律就跑路了。iee
考虑手模样例。你会发现在第 \(2^p\) 次操作时,\(A_i = A_i \oplus A_{i+2^p}\)。这是固定的。
由于序列全 \(0\) 后你再操作也没啥影响。于是按位贪心,每次判断 \(2^p\) 次操作是不是有必要的。然后维护 \(A\) 即可。
CF1903D1
水掉了。
考虑按位贪心。每一位是否能取,那么 \(\mathcal{O(n)}\) 计算贡献即可。时间复杂度 \(\mathcal{O(qn\log n)}\)。能过。
D2 的话就是瓶颈在于计算贡献。那么考虑 SOSDP。不太会跑路了。
CF932D
由于前加入的点不会影响后加入的点,可以预处理。
每个 \(i\) 在往上跳时只会对应一个 \(j\),所以考虑倍增。
倍增维护每个点往上跳 \(2^i\) 步到达的点,路径上的最值。这样可以求出每个 \(i\) 对应的那个唯一合法的 \(j\)。
然后将 \(j,i\) 建边。再进行一次倍增,这样每次询问时跳就好了。还需要维护路径的长度以及点权和。
注意比较恶心的是这题是点权,所以在查询跳的时候 \(x+2^i\) 是不取的。这样你 \(x \leftarrow x+2^i\),就不会重复计算。因此你可能跳到 \(0\) 也是合法的。
P7143
神秘分治题。
考虑分治。容易发现答案只与长度有关。记 \(F_n\) 表示长度为 \(n\) 的答案。考虑区间 \([l,r]\) 怎么贡献。
- 两个端点都在 \([l,mid]\) 或 \([mid+1,r]\) 中。显然这是子问题,分治下去即可。
- 两个端点分别在 \([l,mid]\) 和 \([mid + 1, r]\) 中。首先由于除 \([1,n]\) 外 \(mid\) 和 \(mid+1\) 一定不在同一区间内。因此对于区间 \([x,y]\),覆盖的区间一定是形如 \([x,mid]\) 的关于 \(mid\) 的后缀 和形如 \([mid+1,y]\) 的关于 \(mid+1\) 的前缀。那么统计出 \([x,mid]\) 的后缀 cover 和 \(s_1\),贡献即为 \(s_1 \times [y - (mid + 1) + 1]\)。同理右边的前缀 cover 和 \(s_2\),贡献就是 \(s_2 \times (mid - x + 1)\)。
P11005
我是唐氏。
\(f(u,v) = x\) 等价于 \(u,v\) 只能通过边权 \(\ge x\) 的边连通。从小到大加边即可。
P2048
典题现在才做。
暴力的做法是把所有区间塞进堆然后取 \(k\) 次。但是这样会爆。考虑一个很牛逼的 trick。
由于我们只关心前 \(k\) 大,我们是不是能从当前的某个答案,推出下一个合法的答案,再塞入堆呢?记 \((v,p,l,r,x)\) 表示当前答案最大值为 \(v\),取到的位置为 \(p\),以 \(x\) 为右端点,在 \([l, r]\) 中转移。那么每次取出最大的 \(v\),就可以分裂出两个合法的答案 \((v',p',l,p-1,x)\) 和 \((v'',p'',p+1,r,x)\)。塞入堆中再维护即可。
P3801
容斥好题。
如果你把释放的位置也看做消失的话,那么假设当前范围内有 \(p\) 行被染色,\(q\) 列被染了,就好恰好有 \(p*q\) 个格子重复染了(也就是消失了)。那么做完了。
P8473
不会做糖糖题。
首先每次加入的那一堆线段很烦,考虑啥是有用的,你会发现区间内相隔最远的两个黑点才是有用的,因为这两个黑点间所有点都能被染黑。因此每次加入一个区间,只考虑其间距离最远的两个黑点,将这段区间覆盖即可。每次询问等价于询问是否有白点没有被覆盖。这个用 sgt 维护区间 \(+/- 1\) 即可。
P9691
咋这么难。
首先如果代价为 \(1\) 的话就是超速检测。这里借鉴一下思路。\(F_i\) 表示前 \(i\) 个点,第 \(i\) 个点一定选的最小代价。
考虑 \(i\) 可以从哪转移过来。假设从 \(j\) 转移过来,那么需要满足 \((j,i)\) 中没有完整的区间,这个很好理解。那么对于每个 \(i\),记 \(p_i\) 表示最小的满足条件的 \(j\),这个显然是满足区间右端点 \(<i\) 的左端点最大值,很好维护。那么 \(i\) 就可以从 \((p_i,i)\) 中转移。偷懒写了 sgt,实际上有单调性的。
P8026
不可以总司令。
由于我们只关心两个点是否联通,等价于它们的各个祖先是否相等。于是给每个图赋一个随机值,哈希记录每张图上每个点的祖先之和的哈希值。判断两点祖先是否相等就等价于判断两点哈希值是否相同。
更具体地,记每张图的哈希值为 \(S_i\),假设第 \(i\) 张图的点 \(j\) 的一个祖先为 \(k\),\(j\) 的哈希值就加上 \(S_i * k\)。正确性显然。注意连边时,维护祖先要使用启发式合并。
P3792
bbwzt
异或哈希板子。对于询问,可以根据区间和判断这个区间本应该是啥,然后异或哈希判断即可。
P2536
爆
考虑朴素 dp。每次对于一个文本串,一个模式串进行判断。\(f_{i,j}\) 表示模式串 \(S\) 的前 \(i\) 位,文本串 \(T\) 的前 \(j\) 位能否进行匹配。
分讨 \(S_i\):
- 为字母。平凡。
- 为 \(?\)。显然只关心 \(f_{i-1,j-1}\)。
- 为 \(*\)。不贡献:\(f_{i-1,j}\)。贡献 \(1\) 位:\(f_{i-1,j-1}\)。贡献若干位:\(f_{i-1,k}\)。
这样是 \(\mathcal{O(nmL^2)}\) 的。
考虑当 \(*\) 贡献若干位的时候,实际上可以看成是 \(f_{i,j-1}\),仔细想想就发现这是对的了,这也是一个 trick。这样还是 \(\mathcal{O(nmL)}\) 的,但是已经过题了。
考虑用 trie 优化。先把所有文本串扔进 trie。然后我们用模式串进行匹配。匹配的过程和上面的 dp 是一样的,最终只要统计能走到多少个文本串的末尾就好了,注意要判重。
P1944
小黄题这么难。
\(f_i\) 表示以 \(i\) 为结尾的最长连续合法括号序列。那么如果 \(S_i = S_{i-1-f_{i-1}}\),就有转移 \(f_i = f_{i-1} + 2 + f_{i-2-f_{i-1}}\)。
这个太难想了!不妨求出每个括号是否能被合法匹配,记其为 \(h_i\),这个很好求。那么问题转化为求 \(h_i\) 中最长连续 \(1\) 数量。简单做即可。
P6286
怎么是板子。
考虑直接按加密后字符串顺序排序,那么假设我们有 \(S,T\) 要满足 \(S\) 在 \(T\) 前面,显然只需要满足 \(S\) 最高一位与 \(T\) 不一样的小于 \(T\) 就好了。那么我们就会得到若干条形如 \(x\) 要在 \(y\) 前面的性质,考虑直接连边跑拓扑,有环则无解。做完了。
CF1879D
先做 P3917。那题可以先对 \(A\) 异或一遍前缀,即 \(A_i = A_1\oplus A_2 \oplus \cdots A_i\)。下文中 \(A_i\) 表示异或后的 \(A_i\)。那么按位考虑,思考每一位咋选才能有贡献。实际上,不妨记 \(B_i\) 表示 \(A_i\) 的第 \(x\) 位。那么由于异或可以用类似前缀和来维护,那么 \(B_i\) 中的每个 \(1\) 都可以和 \(0\) 匹配(不管是往前还是往后)。那么那题就做完了。
这题的话也差不多,用 \(0\) 去匹配 \(1\),分别维护前缀 / 后缀中 \(1\) 的下标和即可。
P5662
做不来普及组的题,玉玉了。
考虑一个很妙的性质:假设你在第 \(x\) 天买入,第 \(y\) 天卖出某个商品。那么等价于在第 \(x\) 天买入,第 \(x+1\) 天卖出,第 \(x+1\) 天买入,第 \(x+2\) 天卖出 \(\cdots\) 第 \(y-1\) 天买入,第 \(y\) 天卖出。那么第 \(i\) 天买的东西不妨强制第 \(i+1\) 天卖出。
这样第 \(i\) 天的金币数就只跟第 \(i-1\) 天的金币数有关了。对于第 \(i\) 天,只需要贪心地让第 \(i\) 天买入的商品在第 \(i+1\) 天卖出的利润尽量大就好了。对于每一天分别 dp 就好了。
P11323
又被普及组贪心击杀了。
将炸看成三带一。那么尽量出四张肯定最优。于是用单 / 双张去凑三张即可。做完了。
ABC379F
又被击杀了。
考虑离线下来,按 \(l\) 排序。
对于一栋建筑 \(x\),显然如果它能被 \(l\) 看到,一定能被 \(r\) 看到。
于是维护 \(l\) 右侧能看到的建筑,这个可以用单调栈维护。然后在栈上二分 \(r\) 能看到的即可。
口胡了没写。
P1419
不会普及组 trick。
考虑二分答案。然后你就会了。
P13271
击杀了。
分层图板子。一个 naive 的想法是每个点建 \(k\) 个状态,但是会爆。注意到 \(\sum d_i = m\),由于每个点只有 \(d_i\) 个状态有用,于是只建 \(d_i\) 个。
记 \((u,p)\) 表示 \(u\) 点的第 \(p\) 个状态。对于边 \(u\to v\),且这条是 \(u\) 的第 \(l\) 条边。如果 \(d_v \ge l\),显然可以直接连 \((u,l) \to (v,l)\)。反之,只能 \((u,l) \to (v,d_v)\) 连,且需要维护 \(l \to d_v\) 的代价,前缀和维护。
然后跑最短路。但是你发现这样有个问题,对于某些没有出边的点,我们貌似强制它的最终状态为 \(1\) 的,这是不对的。考虑建反图。在计算 \(x\) 的答案时,枚举所有指向 \(x\) 的点 \(u\),即在原来的图中 \(u \to x\),假设这是第 \(l\) 条边,边权为 \(w\)。用 \(dis_{u,l}+w\) 来更新答案即可。做完了。
P6492
模板题。被击杀了。
考虑 sgt 维护每个节点的前 / 后缀,区间内最大值。然后就是小白逛公园。类似那题多维护的一个 \(sum\),这里需要判断前缀是否为本身。pushup 是这样的:
void pushup(ll k, ll l, ll r)
{
ll mid = l + r >> 1;
ll L = mid - l + 1, R = r - mid;
if (A[mid] != A[mid + 1])
{
if (tc[k << 1].Ls == L) tc[k].Ls = L + tc[k << 1 | 1].Ls;
else tc[k].Ls = tc[k << 1].Ls;
if (tc[k << 1 | 1].Rs == R) tc[k].Rs = tc[k << 1].Rs + R;
else tc[k].Rs = tc[k << 1 | 1].Rs;
tc[k].Ans = max(tc[k << 1].Ans, tc[k << 1 | 1].Ans);
tc[k].Ans = max(tc[k].Ans, tc[k << 1].Rs + tc[k << 1 | 1].Ls);
}
else
{
tc[k].Ls = tc[k << 1].Ls;
tc[k].Rs = tc[k << 1 | 1].Rs;
tc[k].Ans = max(tc[k << 1].Ans, tc[k << 1 | 1].Ans);
}
}
P4198
典题。
显然要考虑斜率。问题转化为单点修改,查询全局最长上升子序列的长度。
对于每个节点,我们维护它区间内的斜率最大值 \(k\),和区间内的答案 \(L\)。在自下而上合并时 \(k\) 很好维护,问题在于 \(L\) 的合并。
显然对于节点 \(x\),它的左儿子的答案可以直接合并。右儿子的话需要我们找到第一个 \(>\) 左儿子的 \(k\) 的 \(\max\) 的点 \(p\),将 \(p\) 后面的答案接上。
记 \(get(l,r,k)\) 表示 \([l,r]\) 内第一个 \(>k\) 的位置开始的序列长度。那么上面 \(x\) 就可以从左儿子的 \(L\) 和右儿子的 get 合并。现在只需要解决 \(get(l,r,k)\)。分讨状态:
- 当前为叶子,平凡。
- \([l,r]\) 中斜率的最大值 \(\le k\),答案显然为 \(0\)。
- \([l,mid]\) 中斜率的最大值 \(> k\),此时 \((mid,r]\) 中的答案必定可行,只需在 \([l,mid]\) 中继续查询。但是注意右儿子的答案需要用 \([l,r]\) 的 \(L\) 减去 \([l,mid]\) 的 \(L\) 来得到。
- \([l,mid]\) 的斜率的最大值 \(\le k\),此时只需在 \((mid,r]\) 中继续查询。
ll get(ll k, ll l, ll r, ld v)
{
ll mid = l + r >> 1;
if (l == r) return tc[k].mx > v;
if (tc[k].mx <= v) return 0;
if (tc[k << 1].mx > v) return get(k << 1, l, mid, v) + tc[k].len - tc[k << 1].len;
return get(k << 1 | 1, mid + 1, r, v);
}
void pushup(ll k, ll l, ll r)
{
ll mid = l + r >> 1;
tc[k].mx = max(tc[k << 1].mx, tc[k << 1 | 1].mx);
tc[k].len = tc[k << 1].len + get(k << 1 | 1, mid + 1, r, tc[k << 1].mx);
}
那么做完了。是 2log 的。
ARC119C
结论题。
操作 \(1\) 无用。手摸一下可以发现一个区间合法当且仅当其交错和为 \(0\),那么将奇数位不变,偶数位 \(\times (-1)\),问题转化为区间和为 \(0\) 的区间个数,简单维护。
P7073
诗人?
用栈来维护,先把表达式树建出来。
由于每次只修改一个点,考虑这个点啥时候对答案有影响?
对于类似 0&0,1|1 这种东西,由于只修改一个点,可以发现无论左右咋变,答案都不变。
那对于这种东西,把它子树内的变量标记为没用的点即可。
实际上要标记的是有用的点。显然。
对于那些对答案有影响的点,容易证明它一定会让答案发生改变,不会出现不变的现象。那么搞定了。
P8815
不会中缀转后缀。
- 左括号直接入栈。
- 右括号,不断弹栈直到弹出左括号。
- 数字直接入栈。
- 操作符,不断弹栈直到栈空或者弹到优先级比它高的字符。
转完后遍历一遍就做完了。
P10115
很典但是把我击杀了的题。
\(f_i\) 表示前 \(i\) 位的答案。枚举当前段的上一个位置 \(j\)。如果 \([j,i]\) 合法就更新 \(f_i = \max\{f_{j-1} + w_i - w_j\}\),否则 \(f_i = \max\{f_{j-1}\}\)。那么做到二次方。
注意到第二项不用枚举。瓶颈在于第一项。把 \(j\) 提出来,\(f_i = \max\{f_{j-1} - w_j\} + w_i\)。其中 \([j,i]\) 要合法,考虑怎么快速维护这个东西。
记 \(c_i\) 表示 \(\max\{f_{j-1} - w_j\}\)。对于右括号,匹配出它对应的左括号,不妨令其为 \(p\)。那么 \(c_i = \max\{c_{p-1}, f_{p-1} - w_p\}\)。还是很显然的。那么做完了。用 stl 会带一只 log。
P8848
钛合金手机。中间忘了,后面忘了。
首先与 A 类似地,\(p,q\) 分别表示 \(1,-1\) 个数。
若 \(p \le q\) 最大子段和为 \(1\)。在 \(-1\) 中插 \(1\) 即可。\(\binom{q+1}{p}\)。
否则,考虑 dp。由于我们要让答案取到下界 \(p - q\),容易证明每个 \(-1\) 必定在两个 \(1\) 之间。那么当前的前缀和就是最大子段和。于是可以 dp。\(f_{i,j}\) 表示用了 \(i\) 个 \(1\),前缀和为 \(j\) 的方案数,很好转移。滚动一下数组就是 \(\mathcal{O(n^2)}\) 的,但常数很小。
P14083
感觉挺简单的。但还是被击杀了。
首先明确 \(f(i)\) 表示 \(i\) 在第几层括号里。其实这个知道也没啥用,方便理解。
先判断 \(p\) 是左括号还是右括号,只需判断 \(p - f(p)\) 的奇偶性即可。这个很好证明,打表也可以发现。
然后假设 \(p\) 是左括号,令其配对的括号为 \(q\)。可以发现 \(p+1\sim q\) 单调不增。那么二分,做完了。
注意判断 \(r-l=1\) 的时候,这样少一次,可以通过。
P11470
没写口胡了一下。
注意到 \(\sum x_i\) 的神秘限制。考虑 \(f_i\) 表示 \(\sum x = i\) 时,\(\sum y\) 的最大值。挺套路。
那么每组询问可以枚举 \(i\),答案就是 \(\min\{a + i,b+f_i\}\)。这样会爆。
考虑二分答案。假设答案为 \(x\)。那么有
\(a+i \ge x, b+f_i \ge x\)。
也就是
\(i \ge x - a\),\(f_i \ge x - b\)。
右边都是定值。那么只需维护 \([x-a,V]\) 的 \(f_i\) 的最大值是否 \(\ge x - b\) 即可。后缀和可以维护。
P9742
又被贪心创飞了。
考虑序列中第一个负数到最后一个正数,显然可以调整使得每一个人都达到正贡献。
考虑一个非负数的连续段 \([l,r]\),我们会牺牲某个人 \(i\) 的代价,让 \(i \leftarrow r\),这样 \([i+1,r]\) 就有贡献了。枚举 \(i\) 取最值即可做到线性。非正数同理。那么做完了。
P11230
首先考虑如果能从一个人转移咋做。显然考虑 \(f_{r,x}\) 表示第 \(r\) 轮 \(x\) 号元素能否做最后一个。
转移的时候,枚举每个 \(i\) 的词典,假设到了 \(x = d_{i,j}\)。那么判断 \(p \in [j-k+1,j-1]\) 中是否有 \(f_{r-1,d_{i,p}} = 1\) 即可。
考虑这个限制。现在我们不仅关心能不能做,我们还要关心它是从哪一行转移的。
\(f_{r,x} = 0/x/-1\) 表示能被两行以上转移,只能被第 \(x\) 行转移 / 不能被转移。
那么随便维护就好了。时间复杂度 \(\mathcal{O(nr + \sum S)}\)。常数略大。
P14362
玉玉了。最失败的一集中最失败的一题。
考虑新建 \(k\) 个点。\(2^k\) 枚举每个城镇,然后直接跑 kruskal,可以得到若干分。
由于只有原图 mst 上的边有用,保留这些边,这样边的量级到了 \(n\)。时间复杂度 \(\mathcal{O(2^kn\log n)}\)。还带点并查集的常数,瓶颈在于排序。可以得到 \(80\) 分。
止步于此了。实际上,每次都加边是很唐的。考虑把所有边都先加上,对于那些没开的城镇的边,不理它即可。这样就少了个 log。带点并查集的常数,可以通过。
有神秘归并做法。不会跑路了。
笑点解析:赛时拼包挂了挂成 \(24\) 分。赛后 10min 会了 \(80\)。
P6000
有点红了。
首先字母串得能匹配。这个很好判。
对于某个左端点,由于字典序最小,我们肯定贪心地把它往右放。
对于区间 \([l,r]\) 容易证明当 \(l, r-1\) 的栈形态相同(或 \(l-1,r\))时,\([l,r]\) 合法。
哈希处理。那么可以 \(\log n\) 来查找每个 \(l\) 对应的 \(r\)。
对于这种括号题,相似的是同样在这篇文章的 CF149D。类似地,我们每次把合法区间分割成两个合法区间。这样更好做。
P1966
排序交换。
把式子拆开,得到 \(a_i^2 + b_i^2 - 2a_ib_i\)。只需最大化 \(a_ib_i\)。排序不等式得 $a_1b_n + a_2b_{n-1} + \cdots + a_nb_1 \le a_?b_? + \cdots a?b? \le a_1b_1 + a_2b_2 + \dots a_nb_n $,其中 \(a,b\) 分别已排序。也就是反序和 \(\le\) 乱序和 \(\le\) 同序和。
于是考虑让 \(a\) 中第 \(k\) 小的和 \(b\) 中第 \(k\) 小的对应。由于我们只关心火柴高度的相对大小,离散化完可以得到两个排列。映射一下跑 LIS 即可。
P5017
补了很久前一直想写的题。
P4581
黑。不过是道随机化好题。
考虑建边。假设第 \(i\) 道题由想法 \(j,k\) 组成,我们将 \(j \to i, k \to i\) 建边。那么对于每个入度不为 \(0\) 的点,我们需要求出有多少个入度为 \(0\) 的点能走到它。直接合并的话会有重复,咋做呢?
下文中称入度为 \(0\) 的点为特殊点。
上述问题是不存在确定性解法的。考虑随机化。我们给每个点随机一个权值 \(W_i\),接下来有两种做法:
Sol1:
对于每个节点,维护能走到它的特殊点的权值 \(\min\)。这个很好 \(\mathcal{O(1)}\) 维护。考虑以下这个东西:
考虑 \(n\) 个均匀随机分布变量 \(a_1,a_2,\cdots a_n \in [1, V]\)。那么 \(\min(a_1,a_2,\cdots a_n)\) 的期望大小为 \(\frac{V}{n+1}\)。也就是值域的 \(n+1\) 等分点。这个可以感性理解。
那么对于点 \(i\),维护能走到它的特殊点的权值 \(\min = w\)。\(w\) 的期望大小就是 \(\frac{V}{k+1}\),其中 \(k\) 为答案。那么 \(\frac{V}{w} - 1\) 即为答案。多跑几次取平均值即可。
实际上,由于除法掉精太厉害了,考虑直接计算每组数据的 \(\sum w\)。直接用 \(\frac{TV}{\sum w}\) 即可。不会证。
Sol2:
刚才那个做法比较有病。
考虑以下这个结论:
考虑 \(n\) 个均匀随机分布变量 \(a_1,a_2,\cdots a_n \in [1, V]\)。不妨记其 \(k\) 小数为 \(a_k\)。那么有期望 \(\frac{a_k}{V} = \frac{k}{n}\)。这个很好证明。
那么有 \(n = \frac{kV}{a_k}\)。做完了。
考虑维护所有权值的区间第 \(k\) 小,这样是更精确的。合并的时候写个归并状物就好了。
P1484
好题。感觉这题很牛逼。
可以写出 \(\mathcal{O(nk)}\) 的 dp。这样没有前途,考虑反悔贪心。
考虑贪心地选择当前价值最大的树 \(a_i\)。但是可能会存在 \(a_{i-1} + a_{i+1} > a_i\) 的情况。这时候就需要反悔。具体地我们把 \(a_i \leftarrow a_{i-1} + a_{i+1} - a_i\),把 \(a_{i-1},a_{i+1}\) 删去。这样假设我们后续又选了这个 \(a_i\),相当于反悔选择 \(a_{i-1} + a_{i+1}\),此时同理处理这个 \(a_i\),后续如果再选,就相当于反悔了上次反悔操作。这个可以感性理解。
链表维护前驱即可。
P3049
又被普及组题目击杀了。
很妙的转化是将起始状态 \(A = [2, 3,5]\) 变成 \(A' = [1,1,2,2,2,3,3,3,3,3]\)。终止状态 \(B\) 同理。这样就变成了一个编辑距离状物的东西。
那么编辑距离。对于操作 \(1,2,3\) 容易转化。时间复杂度 \(\mathcal{O(n^2 \times V),V = 100}\)。
P2748
考虑加强。上面的 dp 没啥前途了。考虑贪心。
注意到土块的数量极少,考虑在这上面做文章。
考虑花坛 \(l\) 的土块单位 \(i\)。假设当前缺土,两种选择:
- 直接买。代价 \(V_i\) 为 \(X\)。
- 从前面拿。假设从前面的 \(j\) 拿。假设 \(j\) 在第 \(k\) 个花坛。那么代价 \(V_i = |k-l| \times Z - V_j\)。
故 \(V_i = \min(X, |k-l| \times Z - V_j)\)。多土的同理。枚举 \(k\) 可以做到 \(\mathcal{O(n^2V^2),V = 100}\)。
观察式子。显然 \(l > k\)。那么 \(V_i = \min(X, lZ-kZ - V_j)\),只需求 \(\max(kZ + V_j)\),用堆维护即可。
CF1852A
性质题。
考虑二分答案。那么维护答案在序列里的排名 \(d\),倒推 \(k\) 天即可。对于第 \(i\) 天,假设当前序列中有 \(l\) 的数 \(< d\)。那么 \(d \leftarrow d - l\)。最后判断 \(d \le 0\) 即可。时间复杂度 \(\mathcal{O((k+n)\log V)}\)。
发现我们做了许多重复性的操作。考虑正推。假设某天后,答案的排名为 \(d\)。假设当前序列中有 \(l\) 个数满足 \(d + i \ge a_i\),该天前的排名就是 \(d + l\)。这个跟二分中的操作本质上相同。那么做到 \(\mathcal{O(n+k)}\)。
实际上,这个过程很好优化。按 \(a_i\) 来考虑。显然对于某些 \(d\),我们会在 \(a_i\) 处一直加 \(i\) 直到到达边界 \(a_{i+1}-1\)。 那么可以计算出次数 \(q = \min(k, \lfloor \frac{a_{i+1} - 1 - d}{i} \rfloor)\)。令 \(d \leftarrow d + q \times i\) 即可。当 \(d > a_n\) 时直接令 \(d \leftarrow d + k \times n\) 即可。严格线性。
ARC197C
P14525
好题。
\(\mathcal{O(n^4)}\) 的暴力是平凡的。
下文中记 \(h\) 表示矩阵行的长度,\(w\) 表示矩阵宽的长度。
考虑钦定 \(h \le w\)。枚举 \(h\) 以及行 \(i\),那么此时矩阵的长必定是由 \([i,i+h-1]\) 平移得到,换句话说,我们已经把矩阵的长给确定了。现在只需要考虑宽。由于长是确定的,对于每一列,都可以看成一个数。只需求长度 \(\ge h\) 的最大子段和即可。这个可以前缀和 + 前缀 \(\min\) 维护。
同理,钦定 \(w \le h\) 再做一遍即可。时间复杂度 \(\mathcal{O(n^3)}\)。
P7840
对于一珂 \(n\) 个点的树,容易发现所有点的度数之和为 \(2n - 2\)。
每个点都至少连一条边,度数至少为 \(1\)。于是我们有 \(n - 2\) 的度数可以分配。
假设对于 \(d_i\),给它分配一个度数,它的贡献会增加 \([(d_i + 1)^2 - d_i^2]v_i = v_i + 2d_iv_i = (2d_i+1)v_i\)。
维护 \((2d_i + 1)v_i\) 的最小值。取出 \(n-2\) 次最小值即可。
P7809
击杀了。
第二问随便做。考虑第一问。容易发现就是找一个分界点 \(x\),使得 \(x\) 前的 \(0\) 数 + \(x\) 后的 \(1\) 数最多。前缀和 + st 维护即可。时间复杂度 \(\mathcal{O(n\log n + m)}\)。我的写法常数略大。最慢点 1.8s。
P9755
好难。暴力不会写。
考虑 A 性质咋做。此时每珂树都有一个固定的生长时间 \(t\)。有一个贪心策略是,考虑按 \(t\) 降序排序。先去种那些 \(t\) 较大的树是更优的。这个可以手玩一下证明。
那么对于当前还没种的,\(t\) 最大的树,从它祖先中深度最小的,还没种的树往下种。可以直接暴力往上跳,由于每个点只会被种一次,做到 1log。结合暴力可以获得 40pts。
考虑链。此时种树顺序是定的。只需考虑每珂树的生长时间。考虑函数 \((l,r,i)\) 表示第 \(i\) 珂树在第 \(l \sim r\) 天能生长多高。分讨即可。那么每个 \(i\) 可以二分出答案。取 \(\max\) 即可。结合上述部分分可以获得 50pts。
考虑二分答案。这样就变成了存在性问题。对于每珂树,预处理出它的最晚种树时间 \(t'\)。那么按 \(t'\) 从小到大考虑,套用 A 性质做法即可。时间复杂度 \(\mathcal{O(n\log n\log V)}\)。
P14566
被击杀了哥们。
记 \(M\) 为 \(A\) 中的最大值。\(M'\) 为 \(A\) 中的次大值。\(N\) 为 \(A\) 中的最小值。
显然我们有一个方案是令 \(p = M + 1\)。这样价值就是 \(M - N\)。
然后考虑怎么做到更优。考虑令 \(p = M\),这样可以得到价值 \(M'\)。
继续考虑。此时我们得到价值 \(\max(M',M-N)\)。注意到 \(a \bmod p \le p\)。所以我们不需要考虑 \(p \le M'\) 的部分。
考虑 \(p\in (M', M)\)。此时代价为 \(\max(M',M\bmod p) - \min(N, M\bmod p)\)。分讨 \(M \bmod p\) 的取值:
- \(M \bmod p > M'\)。此时代价为 \(M \bmod p - N\),由于 \(M \bmod p < p\),即 \(M \bmod p < M\)。即原式 \(< M - N\)。不优。
- \(M \bmod p \le M'\)。此时代价为 \(M' - \min(N, M \bmod p)\)。显然 \(< M'\)。不优。
综上,我们只需考虑 \(M - N\) 与 \(M'\)。做完了。时间复杂度 \(\mathcal{O(n)}\)。
P9118
111。
考虑 \(k \ge 3\),直接枚举到 \(V = 1 \times 10^6\) 即可。平凡。
考虑 \(k = 2\),首先显然是有 \(\sqrt{n}\) 个。但是会有重复的。你考虑某个 \(X = a^2 = p^k\)。显然有 \(2 \mid k\)。那么只需在对 \(k \ge 3\) 做暴力时,记录下 \(2 \mid k\) 的 \(p^k\) 的数量即可。注意要先对 \(2 \mid k\) 的东西做更新。再对 \(2 \nmid k\) 的东西做更新。过了。常数挺大。
发现自己是 zz。只需在更新时直接判断这个数是不是完全平方数即可。更好想。
P5022
考虑树咋做。显然从 \(1\) 出发,每次走字典序最小的点最优。
考虑基环树咋做。断掉某条边后沿用树的做法即可。\(\mathcal{O(n^2)}\)。
使用基环树相关 trick 可以做到 1log 甚至线性。不会基环树 /ll
P5937
口胡了没写。
奇偶性是具有传递性的。每次只需合并 \(l-1,r\)。使用种类并查集。\(f_i\) 表示与 \(i\) 奇偶性相同的集合。\(f_{i+n}\) 表示与 \(i\) 奇偶性不同的集合。
CF939E
大神呐。
推一下式子。首先证明最大值一定被选。
原答案 \(\displaystyle A = \frac{|S| \times \max(S) - \sum S}{|S|}\)。如果最大值没有被选,换上后答案变为
\(\displaystyle S = \frac{|S| \times max(S+ X) - \sum S - X}{|S|}\)
变化的值就是 \(\displaystyle \frac{|S| \times X - X}{|S|}\),显然为正。
那么最大值一定被选。这样只需考虑选择的数量以及选择的和。容易证明一定优先选小的。双指针维护即可。
P6189
和 noip 计划里的某道题挺像。
首先考虑根号分治。令 \(m = \sqrt{n}\),对于小于 \(m\) 的数能凑出来的和我们做完全背包。对于 \(> m\) 的数,由于累积起来很快,可以证明不超过 \(m\) 个就能凑成 \(n\)。
所以考虑 \(n\sqrt{n}\) 的 dp。\(f_{i,j}\) 表示凑成 \(i\),使用了 \(j\) 个数的方案数。简单转移可以做到根号。
枚举和 \(p + q = n\),方案数相乘即可。总的时间复杂度 \(\mathcal{O(n\sqrt n)}\)。
浙公网安备 33010602011771号