CF2065 做题记录

CF2065 做题记录

赛时过了 ABDEF。G 改代码的时候忘删掉一个 continue,一直过不了。E 忘记判 \(k > \max(n, m)\) 调了一年,还吃了一车罚时。C 赛时不会。

总结:烂完了。

A

直接模拟即可。

B

当序列中有两个相邻的相同的数时答案为 \(1\),否则答案为 \(n\)

C

从左到右依次确定每个 \(a_i\),贪心地让 \(a_i\) 在不小于 \(a_{i - 1}\) 的情况下尽可能小。如果到某个地方无法使 \(a_{i} \ge a_{i - 1}\) 则无解。

D

先考虑一个简化问题:给定一个序列 \(\{a\}\),重排使得 \(\sum_{i = 1}^{n} S_{i}\) 最大。

由于 \(\sum_{i = 1}^{n} S_{i} = \sum_{i = 1}^{n} (n - i + 1)a_{i}\),所以越靠前的数被计算的次数越多,因此 \(\{a\}\) 升序排序时最优。

回到这个题,每个序列一开始被计算的部分不完整,之后一直累加整个序列的和。前面不完整的部分无论怎么重排都一样,后面的部分,由于每个序列长度相同,所以直接贪心地让按整个序列的和从小到大排序即可。

形式化地说,答案可以表示为:

\[\sum_{i = 1}^{n} \left((n - i)mS_{i} + \sum_{j = 1}^{m}\sum_{k = 1}^{j} a_{i, k}\right) \]

其中 \(S_{i}\) 表示第 \(i\) 个序列的和。\(\sum_{j = 1}^{m}\sum_{k = 1}^{j} a_{i, k}\) 和序列的顺序无关,所以最大化前面那部分就行,因此按 \(S_{i}\) 降序排列。

E

构造。设串 \(s\) 中有 \(x\)\(0\)\(y\)\(1\),记 \(f(s) = |x - y|\),即题目中的“balance-value”。

先考虑无解情况:对于整个串,无论怎么排序,都有 \(f(s) = |n - m|\),所以当 \(k < |n - m|\) 时无解,这是 \(k\) 过于小的极端情况。另一个极端是 \(k > \max(n, m)\),此时同样无解。

下面不妨设 \(n > m\)。容易构造出策略:先放 \(k\)\(0\),那么这个长为 \(k\) 的子串保证的整个串 \(f(s) = k\)。放完之后 \(n \gets n - k\)。之后放上多出来的 \(1\),直到 \(n = m\)。最后放上 \(n\)\(01\)(或 \(10\),根据之前最后一个字符而定)即可。容易发现这一定满足要求。

F

如果一条路径存在绝对众数 \(x\),那么 \(x\) 出现的次数至少为 \(2\)。可以发现在这个路径中,要么有两个相邻的 \(x\),要么有一个点同时和两个 \(x\) 相邻。(严谨证明参见官方题解,用了反证法。)而实际上,“两个相邻的 \(x\)”本身就已经构成了以 \(x\) 为绝对众数的路径,“有一个点同时和两个 \(x\) 相邻”同理,因此只需要判断这两种情况,这是简单的。时间复杂度 \(O(n + m)\)

G

赛时没有想清楚就开始写,浪费了很多时间!一定要把逻辑捋顺再写代码啊。

首先,由于 \(\operatorname{lcm}(a, b)\) 相当于把 \(a\)\(b\) 的质因数分解上的每个指数取 \(\max\),所以如果 \(\operatorname{lcm}(a, b)\) 是半质数,那么 \(a, b\) 一定都是质数或半质数,因此只需考虑这些数。

进一步,如果 \(\operatorname{lcm}(a, b)\) 是半质数,那么 \(a, b\) 必然满足以下三种情况之一(设 \(p, q \in \mathbb{P}\)):

  1. \(a = b = pq\)\(p, q\) 可以相等)
  2. \(a = p, b = pq\)\(a, b\) 反过来同理,\(p, q\) 可以相等)
  3. \(a = p, b = q\)\(p \neq q\)

由于值域为 \(n\),所以筛出 \(n\) 以内所有的素数,开一个桶记录每个数出现的次数,枚举所有的 \(p\)\(pq\) 类型的数,再用组合公式统计一下即可。

H

计数题!难点主要在于第一步的转化,后续的数据结构优化是顺理成章的。

按子序列计数显然是没有前途的,因为子序列的数量是 \(O(2^{n})\)。按照计数问题的套路,我们先改变计数对象

对于一个序列,它的权值是其极长的 \(0\)\(1\) 连续段个数,所以我们不妨想想这些极长连续段是怎么出现的。除了第一个极长连续段,所有极长连续段的的开头第一个数必然和它前面一个数不同。也就是说,两个相邻的不同数贡献一个连续段。形式化的表述为:

\[f(s) = 1 + \sum_{i = 1}^{n - 1} [s_{i} \neq s_{i + 1}] \]

因此可以枚举值不同的数构成的数对 \((i, j)\)\(i < j\)\(s_{i} \neq s_{j}\))。对于一对 \((i, j)\),它对总答案的贡献就是使得 \(s_{i}\)\(s_{j}\) 相邻的子序列个数。那么有多少个这样的子序列呢?这相当与钦定 \(s_{i}\)\(s_{j}\) 必须选,\(s_{i + 1} \sim s_{j - 1}\) 必须不选,其它位置可以选也可以不选。因此 \((i, j)\) 对答案的贡献为 \(2^{i - 1 + n - j}\),总答案为:

\[\boxed{ans = \sum_{i = 1}^{n} \left(2^{n - i} + \sum_{j = i + 1}^{n} [s_{i} \neq s_{j}] \cdot 2^{i - 1 + n - j}\right)} \]

(加上 \(2^{n - i}\) 是考虑到 \(s_{i}\) 作为整个子序列的开头时的贡献,此时只有 \(i + 1 \sim n\) 的位置可以任意选。)这样就导出了一个 \(O(qn^2)\) 的做法。

考虑优化。把上式提出公因式变成

\[ans = \sum_{i = 1}^{n} \left(2^{n - i} + 2^{i - 1} \cdot \sum_{j = i + 1}^{n} [s_{i} \neq s_{j}] \cdot 2^{\ n - j}\right) \]

预处理一下 \(2^{n - j}\) 的后缀和,就可以做到 \(O(qn)\) 的时间复杂度。这里的瓶颈主要在于每次修改的时候都要重新算一遍后缀和。

到了这一步,已经可以 \(O(n)\) 计算出在没有修改时的答案。因此我们先计算出未修改时的答案,然后考虑每次修改对答案的影响,在把答案加上这个影响。

由于我们每次修改一个位置的值,因此我们写出一个位置 \(p\) 对答案的贡献 \(g(p)\)

\[g(p) = 2^{n - p} + 2^{n - p} \cdot \sum_{i = 1}^{p - 1} [s_{i} \neq s_{p}] \cdot 2^{i - 1} + 2^{p - 1} \cdot \sum_{i = p + 1}^{n} [s_{i} \neq s_{p}] \cdot 2^{n - i} \]

第一项代表 \(p\) 作为子序列第一个值时的贡献,第二项计算形如 \((i, p)\)\(i < p\))的数对的贡献,第三项计算形如 \((p, i)\)\(p < i\))的数对的贡献。

(值得一提的是,\(ans \neq \sum_{i = 1}^{n} g(i)\),因为 \(g(i)\) 统计了所有包含 \(i\) 的数对的贡献,所以对于每个数对 \((i, j)\),它在算 \(g(i)\)\(g(j)\) 的时候都会被统计到,这样会算重。因此计算 \(ans\) 的时候一定要保证枚举的数对是有序的。但修改的时候我们考虑 \(i\) 的所有贡献,所以任何包含 \(i\) 的数对都要被计入其中。)

每次修改时,先减去 \(p\) 原来的贡献 \(g(p)\),再加上新的贡献 \(g'(p)\)\(\sum_{i = 1}^{p - 1} [s_{i} \neq s_{j}] \cdot 2^{i - 1}\)\(\sum_{i = p + 1}^{n} [s_{i} \neq s_{j}] \cdot 2^{n - i}\) 这两项需要单点修改和区间求值,用树状数组维护即可。(为了计算 \([s_{i} \neq s_{j}]\) 这个因子,需要开两组树状数组分别维护 \(0\) 位置和 \(1\) 位置的值。说“两组”是因为每组都要开两个线段树分别维护 \(2^{i -1 }\) 的和及 \(2^{n - i}\) 的和。)

综上所述,我们做到了 \(O(n + q \log n)\) 的时间复杂度。AC 记录

posted @ 2025-02-11 11:49  DengStar  阅读(75)  评论(0)    收藏  举报