Constructive Algorithms
CF1709C
- 判断是否是唯一解一般都是先构造出一组解再尝试做变换得到另一组解。
- 令左括号为 +1,右括号为 -1,求前缀和,合法相当于前缀和数组每一位都大于等于 0,且最后一位恰好为 0。
- 贪心地将所有左括号填在右括号前,该方案一定合法。
- 任选一个左括号和一个右括号交换,相当于将它们中间一段区间的前缀和数组减 2。
- 贪心地取最右边的左括号和最左边的右括号交换即可。
- Code
CF1705D
算法 1
- 取长度为
n
−
1
n - 1
n−1 的串
s ‾ = ( s 1 ⊕ s 2 ) ( s 2 ⊕ s 3 ) … ( s n − 1 ⊕ s n ) t ‾ = ( t 1 ⊕ t 2 ) ( t 2 ⊕ t 3 ) … ( t n − 1 ⊕ t n ) \begin{aligned}\overline s &= (s_1 \oplus s_2)(s_2 \oplus s_3)\dots(s_{n - 1}\oplus s_n) \\\overline t &= (t_1 \oplus t_2)( t_2 \oplus t_3)\dots(t_{n - 1}\oplus t_n) \\\end{aligned} st=(s1⊕s2)(s2⊕s3)…(sn−1⊕sn)=(t1⊕t2)(t2⊕t3)…(tn−1⊕tn) - 则原操作相当于每次选择一对相邻的 0 和 1 交换。
- 记串 s ‾ \overline{s} s 中 1 的位置从小到大依次为 a 1 , a 2 , … , a p a_1, a_2, \dots, a_p a1,a2,…,ap,串 t ‾ \overline{t} t 中 1 的位置从小到大依次为 b 1 , b 2 , … , b q b_1,b_2,\dots,b_q b1,b2,…,bq,显然 p ≠ q p \neq q p=q 时无解,同时最少操作步数为 ∑ i = 1 p ∣ a i − b i ∣ \sum \limits_{i = 1}^{p}|a_i - b_i| i=1∑p∣ai−bi∣。
- Code
算法 2
- 本质上与算法 1 相同。
- 不难发现,每次操作只会让原串中连续的 1 (以下简称为连续段)伸长和缩短,但连续段数目保持不变,不同的连续段之间无法合并。
- 因此贪心地将 s s s 中每个连续段移到 t t t 中对应连续段的位置即可。
- Code
CF1338B
- 为方便处理,选择一个不为叶子的结点为根,同时任意两个结点之间路径的异或和为它们分别到根结点路径的异或和的异或和。
- 考虑最小的答案,注意到如果所有叶子结点的深度均为偶数或均为奇数(即所有叶子结点之间的路径长度均为偶数),则全填上 1 即可满足条件。若叶子结点的深度既有奇数又有偶数,则先交替地填上 1 和 2,再对深度为奇数的点(根结点深度为 0),将其到父结点的边改为 3 即可满足条件。
- 考虑最大的答案,注意到若两个叶子结点的 LCA \text{LCA} LCA 不同时为它们的父结点,因为可以填任意大的数值,则将它们到它们的 LCA \text{LCA} LCA 的所有边填上的方案有足够多种,因此除了父结点相同的叶子结点到它们父结点的边必须填成相同的之外,剩下的所有边总能找到一种方案使得每条边的数值均不同。
- Code
CF449C
- 首先排除 1 和大于 ⌊ n 2 ⌋ \lfloor \frac{n}{2} \rfloor ⌊2n⌋ 的素数配对的可能。
- 容易想到按照素数的倍数分组两两配对,但如果有落单的情况很不好处理。
- 考虑从大到小枚举素数 p p p,此时 2 p 2p 2p 一定还未配对,如有落单的情况就将 2 p 2p 2p 取出,剩下两两配对,则最终所有取出的数均有公因子 2,两两配对即可。
- Code
CF1704E
- 注意到足够多轮后 a i > 0 a_i > 0 ai>0 的点会形成一个连通块,并且之后每次只能使 a i a_i ai 的总和减少 1,因此之后所需的时间就为此时 a i a_i ai 上的每个 1 移动到那个唯一的出度为 0 的点上的方案数的总和。
- 先暴力模拟 n n n 轮,易知此时 a i > 0 a_i > 0 ai>0 的点一定会形成一个连通块,拓扑排序后统计方案数即可。
- 时间复杂度 O ( n 2 ) \mathcal O(n^2) O(n2)。
- Code
CF1705F
- 先问出 T T T … T TTT\dots T TTT…T,显然对于某一道题目,可以将该位置改成 F F F 一次询问得到答案。
- 考虑将每两个问题合在一起询问,即对 1 ≤ i ≤ ⌊ n 2 ⌋ 1 \le i \le \lfloor \frac{n}{2}\rfloor 1≤i≤⌊2n⌋ ,将 T T T … T TTT\dots T TTT…T 中第 2 i − 1 2i - 1 2i−1 道问题和第 2 i 2i 2i 道问题改为 F F F 后询问,比较和 T T T … T TTT\dots T TTT…T 的差异,则很容易区分两道问题都是 T T T 或者 F F F 的情况,但是 F T FT FT 和 T F TF TF 的情况则难以区分,该情况下再增加一次询问,即可得到期望询问次数为 3 4 n \frac{3}{4}n 43n 的算法。
- 考虑问出 F T FT FT 和 T F TF TF 的情况,实际上知道其中一个的答案就能确定另一个,因此每次将该情况下其中一道问题取出,重新随机分组,不断进行上述的算法流程,即可得到期望询问次数为 2 3 n \frac{2}{3}n 32n 的算法。
- 若要得到上界为 2 3 n \frac{2}{3}n 32n 的算法,我们还需要转变一下思路。
- 先问出 T T T … T TTT\dots T TTT…T 和 T F T F … T TFTF\dots T TFTF…T,对 1 ≤ i ≤ ⌊ n 3 ⌋ 1 \le i \le \lfloor \frac{n}{3}\rfloor 1≤i≤⌊3n⌋,将 T T T … T TTT\dots T TTT…T 中第 2 i − 1 2i - 1 2i−1 道问题和第 2 i 2i 2i 道问题改为 F F F 后询问,比较和 T T T … T TTT\dots T TTT…T 的差异,若为 F T FT FT 和 T F TF TF,在 2 ⌊ n 3 ⌋ < i ≤ n 2 \lfloor \frac{n}{3} \rfloor < i \le n 2⌊3n⌋<i≤n 中任选一道题,将 T F T F … T TFTF\dots T TFTF…T 这三个位置对应的字母由 T T T 变 F F F, F F F 变 T T T,再询问一次,因为前两个问题不是 T F TF TF 就是 F T FT FT,两次的差异只能为 ± 2 \pm2 ±2,而第三个问题的差异只能为 ± 1 \pm 1 ±1,故可以在一次询问中区分出来,即每三个问题只需要两次询问,上界为 2 3 n \frac{2}{3}n 32n。
- Code
Dynamic Programming
CF1348E
- 可以求出答案的上下界:
a n s R = ⌊ ∑ i = 1 n a i + ∑ i = 1 n b i k ⌋ a n s L = ⌊ ∑ i = 1 n a i k ⌋ + ⌊ ∑ i = 1 n b i k ⌋ \begin{aligned} ans_R &= \lfloor \frac{\sum \limits_{i = 1}^{n}a_i + \sum \limits_{i = 1}^{n}b_i}{k}\rfloor \\ ans_L &= \lfloor \frac{\sum \limits_{i = 1}^{n}a_i}{k} \rfloor + \lfloor \frac{\sum \limits_{i = 1}^{n}b_i}{k}\rfloor \\ \end{aligned} ansRansL=⌊ki=1∑nai+i=1∑nbi⌋=⌊ki=1∑nai⌋+⌊ki=1∑nbi⌋ - 下界 a n s L ans_L ansL 表示不考虑能将同一颗树的红梅和蓝莓放进同一个篮子的情况。
- 注意到 a n s R ans_R ansR 至多比 a n s L ans_L ansL 多 1,若 a n s L = a n s R ans_L = ans_R ansL=ansR,我们可以直接输出答案。
- 因此我们只需要判断答案是否能取到上界,即是否能使与同一棵树的蓝莓放在同一个篮子里的红梅总数模 k k k 的结果落在区间 [ k − ( ∑ i = 1 n b i m o d k ) , ∑ i = 1 n a i m o d k ] [k - (\sum \limits_{i = 1}^{n}b_i \mod k), \sum \limits_{i = 1}^{n}a_i \mod k] [k−(i=1∑nbimodk),i=1∑naimodk]。
- 设 f i , j f_{i,j} fi,j 表示已经考虑了 i i i 棵树,能否使与同一棵树的蓝莓放在同一个篮子里的红梅总数模 k k k 的结果为 j j j。
- 转移即枚举第 i i i 棵树上与蓝莓放在同一个篮子的红梅数 t t t,这些红梅必定只会被放在一个篮子里(否则一定能再凑出一个全是红梅或全是蓝莓的篮子)。
- 因此对于
0
≤
t
<
k
0 \le t < k
0≤t<k,若满足
t
≤
a
i
t \le a_i
t≤ai 且
k
−
t
≤
b
i
k - t \le b_i
k−t≤bi,有转移
f i − 1 , j → f i , j f i − 1 , j → f i , ( j + t ) m o d k \begin{aligned} f_{i - 1,j} &\to f_{i,j} \\ f_{i - 1, j}&\to f_{i,(j+t)\mod k} \\ \end{aligned} fi−1,jfi−1,j→fi,j→fi,(j+t)modk - 时间复杂度 O ( n k 2 ) \mathcal O(nk^2) O(nk2),前缀和优化可做到 O ( n k ) \mathcal O(nk) O(nk)。
- Code
CF1511G
- 显然是 Nim Game,设 f j , i = ⨁ i ≤ c k ≤ i + 2 j − 1 ( c k − i ) , s u m i = ∑ [ c k ≤ i ] f_{j,i} = \bigoplus \limits_{i \le c_k \le i+2^j-1} (c_k - i), sum_i = \sum [c_k \le i] fj,i=i≤ck≤i+2j−1⨁(ck−i),sumi=∑[ck≤i]。
- 转移过程中可独立计算最高位的贡献:
f j , i = f j − 1 , i ⊕ f j − 1 , i + 2 j − 1 ⊕ ( [ ( s u m i + 2 j − 1 − s u m i + 2 j − 1 − 1 ) m o d 2 = 1 ] × 2 j − 1 ) f_{j,i} = f_{j - 1,i} \oplus f_{j - 1,i + 2^{j - 1}} \oplus ([(sum_{i + 2^j - 1} - sum_{i + 2^{j - 1} - 1})\mod 2 = 1] \times 2^{j - 1}) fj,i=fj−1,i⊕fj−1,i+2j−1⊕([(sumi+2j−1−sumi+2j−1−1)mod2=1]×2j−1) - 询问时以类似转移的方式计算答案即可。
- Code
Greedy
CF1344D
算法 1
- 令 f i ( x ) = x ( a i − x 2 ) f_i(x) = x(a_i - x^2) fi(x)=x(ai−x2)。
- 如果没有和的限制,很容易求出最值位置 x i = a i 3 x_i = \sqrt \frac{a_i}{3} xi=3ai,令 b i b_i bi 取 f i ( ⌊ x i ⌋ ) f_i(\lfloor x_i \rfloor) fi(⌊xi⌋) 和 f i ( ⌈ x i ⌉ ) f_i(\lceil x_i \rceil) fi(⌈xi⌉) 中较大的位置即可。
- 若
∑
i
=
1
n
b
i
=
k
\sum \limits_{i = 1}^{n} b_i = k
i=1∑nbi=k,则
∑
i
=
1
n
b
i
(
a
i
−
b
i
2
)
\sum \limits_{i = 1}^n b_i (a_i - b_i^2)
i=1∑nbi(ai−bi2) 最大可等价为下面的形式
∑ i = 1 n b i ( a i − b i 2 ) + C k = ∑ i = 1 n b i ( a i + C − b i 2 ) = ∑ i = 1 n g i ( b i ) \begin{aligned} &\sum \limits_{i = 1}^{n}b_i(a_i - b_i^2) + Ck \\ = &\sum\limits_{i = 1}^{n}b_i(a_i + C - b_i^2)\\ = &\sum\limits_{i = 1}^{n} g_i(b_i) \end{aligned} ==i=1∑nbi(ai−bi2)+Cki=1∑nbi(ai+C−bi2)i=1∑ngi(bi) - 其中
g
i
(
x
)
=
x
(
a
i
+
C
−
x
2
)
,
C
∈
R
g_i(x) = x(a_i + C - x^2), C \in \mathbb R
gi(x)=x(ai+C−x2),C∈R,其最值位置
x
i
x_i
xi 满足
- 当 a i + C ≥ 0 a_i + C \ge 0 ai+C≥0 时, x i = min { a i , a i + C 3 } x_i = \min\{a_i, \sqrt\frac{a_i + C}{3}\} xi=min{ai,3ai+C}。
- 当 a i + C < 0 a_i + C < 0 ai+C<0 时, x i = 0 x_i = 0 xi=0。
- 用 Lagrange \text{Lagrange} Lagrange乘数法 也可以得到相同的结果。
- 因此可以二分得到 C C C,恰使 ∑ i = 1 n x i = k \sum \limits_{i = 1}^{n}x_i = k i=1∑nxi=k。
- 先令 b i = ⌊ x i ⌋ b_i = \lfloor x_i \rfloor bi=⌊xi⌋,然后将 b i b_i bi 按 Δ i = f i ( b i + 1 ) − f i ( b i ) \Delta_i = f_i(b_i + 1) - f_i(b_i) Δi=fi(bi+1)−fi(bi) 从大到小排序,取前 k − ∑ i = 1 n b i k - \sum \limits_{i = 1}^{n}b_i k−i=1∑nbi 个增加 1 即可。
- 注意精度误差的处理。
- Code
算法 2
- 考虑一个暴力做法,初始时令所有 b i = 0 b_i = 0 bi=0,用堆维护 Δ i \Delta_i Δi(定义同上),每次取 Δ i \Delta_i Δi 最大的增加 1,共增加 k k k 次。
- 不难发现每次取的 Δ i \Delta_i Δi 是单调不增的,因此我们可以二分最后一次取的 Δ i \Delta_i Δi, b i b_i bi 可以通过解二元一次方程计算出来。
- 因为每次取的 Δ i \Delta_i Δi 并非单调减,最终的 ∑ i = 1 n b i \sum \limits_{i = 1}^{n}b_i i=1∑nbi 取值范围可能是一个区间,我们可以先二分得到一个 ∑ i = 1 n b i ≤ k \sum \limits_{i = 1}^{n}b_i \le k i=1∑nbi≤k 且最后取的 Δ i \Delta_i Δi 最大的解,剩下的处理方法同 算法 1。
- Code
CF1775E
算法 1
- 将序列中的 0 直接删除,并将同符号的相邻数字合并,可得到一个正负号交替的序列。
- 不难得到一种贪心的策略:
- 取当前序列中绝对值最小的数的绝对值为 m x mx mx。
- 将序列中每个数的绝对值减去 m x mx mx。
- 此时序列会产生新的 0,删除 0 后需将之后出现的同符号相邻数字合并。
- 可用链表维护这一序列,并通过
set
维护序列中的数的绝对值,将序列中每个数减去一个值可通过全局标记实现。 - 由于每个数只会被删除一次,总时间复杂度 O ( n log n ) \mathcal O(n\log n) O(nlogn),实现较为繁琐。
- Code
算法 2
- 取原序列的前缀和序列 b b b,此时原操作变为选择若干个位置,同时 +1/-1。
- 显然答案为 max { 0 , max 1 ≤ i ≤ n { b i } } + max { 0 , max 1 ≤ i ≤ n { − b i } } \max\{0,\max\limits_{1 \le i \le n}\{b_i\}\} + \max\{0,\max\limits_{1 \le i \le n}\{-b_i\}\} max{0,1≤i≤nmax{bi}}+max{0,1≤i≤nmax{−bi}}。
- Code