CodeForces 合集 I
1838D. Bracket Walk (*2100)
Algorithms:STL, 思维
https://codeforces.com/contest/1838/problem/D
题意
给定一个初始的括号序列 \(S\),\(q\) 次修改,每次修改一个位置 \(u\)(如果 \(S_u =\) (
,那么改成 )
,反之亦然),修改将会 永久化。
判断每一次修改后的 \(S\) 是否合法。
- 合法:存在一个序列 \(A = (a_1, a_2, \dots, a_m)\),满足 \(a_1 = 1, a_m = |S|\),且对于每一个 \(1 \leq i < m\),有 \(|a_i - a_{i + 1}| = 1\),满足 \(S_{a_1}S_{a_2}\dots S_{a_m}\) 是一个合法的括号序列。
\(1 \leq |S|, q \leq 2 \times 10^5\)。
题解
如果 \(n\) 是奇数,那么怎么往回走字符串长度都只是加上偶数,所以无解。
字符串的理想状态就是 ()()()()()...
,把与当前字符串与理想状态的不同的位置加到一个集合里边,设为 \(S\)。
将 \(S\) 从大到小排序。
如果 \(s_{S_1}\) 是 )
,那么无解,因为这样会造成前 \(S_1\) 个 (
的数量小于 )
,造成不合法。
如果 \(s_{S_{|S|}}\) 是 (
,那么也无解,因为这样会造成 (
过多,也不合法。
剩下的情况就是 YES
。
https://codeforces.com/contest/1838/submission/210356117
1838E. Count Supersequences (*2500)
Algorithms:组合数学,递推
https://codeforces.com/contest/1838/problem/E
题意
给定 \(n, m, k\),一个长度为 \(n\) 的序列 \(a = (a_1, a_2, \dots, a_n)\),满足 \(1 \leq a_i \leq k\),求有多少个序列 \(b = (b_1, b_2, \dots, b_m)\),满足 \(a\) 是 \(b\) 的一个子序列,并且 \(1 \leq b_i \leq k\),答案对 \(10^9 + 7\) 取模。
\(1 \leq n \leq 2 \times 10^5, n \leq m \leq 10^9, 1 \leq k \leq 10^9\)。
题解
Conclusion. 答案只与 \(n, m, k\) 有关,与 \(a\) 无关。
因为答案与 \(a\) 无关,所以我们可以将 \(a\) 看作 \(a' = (1, 1, \dots, 1)\)(\(n\) 个 \(1\))。
考虑计算不包含 \(a'\) 的个数。
只需要枚举 \(1\) 的个数即可,注意不能超过 \(n - 1\)。
那么不包含 \(a'\) 的方案数就是:
因为 \(m\) 是 \(10^9\) 级别的,所以考虑递推组合数:
所以从 \(\displaystyle \binom{m}{0}\) 开始递推即可。
所以最后答案就是:
https://codeforces.com/contest/1838/submission/210351122
1843F2. Omsk Metro (hard version) (*2300)
Algorithms:树上倍增,LCA,tricks
https://codeforces.com/contest/1843/problem/F2
题意
给定一棵树(初始只有 \(1\) 个节点),每个节点有权值 \(w_i(w_i \in \{-1, 1\})\),有 \(q\) 次操作:
-
+ x v
,如果当前有 \(k\) 个节点,那么新增节点 \(k + 1\),使得 \(k + 1\) 的祖先为 \(x\),并且 \(w_{k + 1} = v\)。 -
? x v k
,查询 \(u, v\) 之间是否存在点对 \((u', v')\),使得 \(u' \to v'\) 包含在 \(u \to v\) 之中,并且 \(u', v'\) 路径上的 \(w\) 的和为 \(k\),若存在输出YES
,否则输出NO
。
\(1 \leq q \leq 2 \times 10^5\)。
题解
首先补充一个求最大(也可以求最小)子段和的方法:
struct info {int L, R, preL, preR, sufL, sufR, sum;};
L
代表整个序列的最小子段和,R
代表整个序列的最大子段和,preL
代表最小前缀和,preR
代表最大前缀和,sufL
代表最小后缀和,sufR
代表最大后缀和,sum
代表整个序列的加和。
inline info operator + (info l, info r) {
info mid;
mid.preL = min (l.preL, l.sum + r.preL);
mid.preR = max (l.preR, l.sum + r.preR);
mid.sufL = min (r.sufL, r.sum + l.sufL);
mid.sufR = max (r.sufR, r.sum + l.sufR);
mid.sum = l.sum + r.sum;
mid.L = min ({l.L, r.L, l.sufL + r.preL});
mid.R = max ({l.R, r.R, l.sufR + r.preR});
return mid;
}
代码很简单,请读者自行解读()。
然后考虑预处理 \(fa_{u, i}\) 表示 \(u\) 的第 \(2^i\) 级祖先是谁,还有 \(depth_u\) 表示 \(u\) 的深度。
根据 \(fa, depth\),我们可以求出两个点的 LCA 了。
设 \(bin_{u, i}\) 表示 \(u\) 包含自身向上 \(2^i\) 个点组成的序列的 info
(序列的第一个元素是深度最小的那一个)。
设 \(bin2_{u, i}\) 表示 \(u\) 包含自身向上 \(2^i\) 个点组成的序列的 info
(序列的第一个元素是深度最大的那一个)。
然后就可以倍增求出了。
然后我们只需要将两边的信息合并即可,因为 \(w_i \in \{-1, 1\}\),所以路径内的子路径的 \(w\) 之和组成的序列(去重后)是一个公差为 \(1\) 的等差数列,所以我们只需要知道可以取到的最小值和最大值即可。
加点的时候只需要预处理 \({fa/bin/bin_2}_{k + 1, 0}, {fa/bin/bin_2}_{k + 1, 1}, \dots, {fa/bin/bin_2}_{k + 1, 19}\) 即可。
时间复杂度 \(O(n \log n)\)。
https://codeforces.com/contest/1843/submission/210556387
1849D. Array Painting (*????)
Algorithms:贪心, 思维
https://codeforces.com/contest/1849/problem/D
题意
给定一个长度为 \(n\) 的数列 \(a_1, a_2, \dots, a_n\),满足 \(0 \leq a_i \leq 2\),初始每个数都是蓝色的,每一次可以以 \(1\) 的花费将一个蓝色的数染成红色,还可以将一个大于 \(0\) 的红色的数减去 \(1\),并将旁边的一个蓝色的数染成红色。
求将所有数染成红色的最小花费。
\(1 \leq n \leq 2 \times 10^5\),\(0 \leq a_i \leq 2\)。
题解
首先我们发现 \(0\) 将每一段极长的连续的 \(1, 2\) 分成了几段。把连续的 \(1, 2\) 段缩成点:最大值为 \(2\) 缩成 \(2\),全是 \(1\) 并且至少为 \(2\) 的缩成 \(1\),并做标记;否则缩成 \(1\),不做标记。
-
如果缩成 \(2\),可以往左延伸,往右延伸,直到碰到 \(0\),如果下标为 \(u\),那么 \(u - 1\) 和 \(u + 1\) 也可以被染色,花费 \(1\)。
-
如果缩成 \(1\),无标记,往左和往右延伸皆可,选 \(1\) 个未被染色的数染色即可,花费为 \(1\)。
-
如果缩成 \(1\),有标记,说明可以往左往右并花费 \(2\),或者与无标记的一个效果。如果使用后者的决策,那么最后如果有其中一个未被染色,那么花费 \(1\) 的代价,总共花费 \(1 + 1 = 2\) 的代价,因为这个数还可能被别的染色,所以使用后者的决策一定是不劣的。所以按第二种情况处理即可。