ABC435G. Domino Arrangement

  • NN 个格子,最初都没有被涂色。
  • MM 种颜色,第 ii 种颜色可以用来涂区间 [Li,Ri][L_i,R_i] 中的任意多个格子,每种颜色的极长连续段长度恰好为 2,但可以有多个不相连的连续段。
  • 求涂色方案。
  • N,M5×105N,M \leqslant 5 \times 10^{5}

状态:前 ii 个格子已涂色的子问题,

  • fj,1/2f_{j, 1/2}:最后一个格子涂了颜色 jj,且当前颜色 jj 的连续段长度为 1/21/2 的方案数。
  • gg:最后一个格子不涂任何颜色的方案数。

此外用扫描线维护 cjc_{j}:最后一个格子为颜色 jj 是否合法,即是否满足 LjiRjL_j \le i \le R_j

转移:

  • fj,2fj,1f_{j, 2} \leftarrow f_{j, 1}
  • fj,1fj,2+g+fj,2f_{j, 1} \leftarrow - f_{j, 2} + g + \sum f_{j, 2}
  • gg+fj,2g \leftarrow g + \sum f_{j, 2}

维护线段树,每个叶子节点存储 (fj,1,fj,2,cj)(f_{j, 1}, f_{j, 2}, c_j),上述流程可重构为:

(fj,1fj,2cj)(01g+f,2100001)(fj,1fj,2cj)\begin{pmatrix} f_{j, 1} \\ f_{j, 2} \\ c_j \end{pmatrix} \leftarrow \begin{pmatrix} 0 & -1 & g + \sum f_{*, 2} \\ 1 & 0 & 0 \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} f_{j, 1} \\ f_{j, 2} \\ c_j \end{pmatrix}

答案:g+f,2g + \sum f_{*, 2}。复杂度 O(NlogM)\mathcal{O}(N \log M)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Lazy Tag Monoid
struct Lazy {
Matrix mat = identity();
void apply(const Lazy& x) {
mat = x.mat * mat;
}
};

// Data Monoid
struct Info {
Vector f {};
void apply(const Lazy& x) {
f = x.mat * f;
}
};

Info operator + (const Info& l, const Info& r) {
return { l + r };
}

void solve() {
LazySegmentTree<Info, Lazy> tree(M);
Z g = 1;
for (int i = 0; i < N; i++) {
auto [f1, f2, c] = tree.query(0, M).f;

for (auto [idx, opt] : events[i]) {
auto [f1, f2, c] = tree.query(idx, idx + 1).f;
if (opt) {
tree.modify(idx, {f1, f2, opt});
} else {
tree.modify(idx, {0, 0, 0});
}
}

tree.update(0, M, {{{
{0, -1, g + f2},
{1, 0, 0},
{0, 0, 1}
}}});
g = g + f2;
}

auto [f1, f2, c] = tree.query(0, M).f;
Z ans = g + f2;
}

CF147B. Smile House

有向图,求图上点数最小的正环的大小。

fd,i,jf_{d, i, j}:至多 2d2^{d} 边,从 iijj 的最大权值。

跳跃式倍增尝试使用 xx 条边,是否存在正环,即是否存在 gi,i>0g_{i, i}>0

CF1808E. Minibuses on Venus

题意:给定 N,KN,K,求满足存在 ii,使得 2aiai(modK)2 a_{i} \equiv \sum a_{i} \pmod K 的序列数量。

容斥,求对于任意的 ii,都满足 2aiS2a_i \neq S 的方案数。

枚举 SS,设 fi,jf_{i, j} 为前 ii 位,数字和是 jj 的方案数。

转移:fi,j+lfi1,lf_{i, j + l} \gets f_{i - 1, l},其中 2l≢S2l \not\equiv S

【快速幂】定义转移矩阵 TT,其中 Tj,j+l=[2lS]T_{j, j + l} = [2 l \neq S]。设 f0=[1,0,,0]f_{0}=[1, 0, \dots, 0],答案是 f0×Tnf_{0} \times T^n 的第 SS 项,复杂度 O(K4logN)\mathcal{O}(K^4\log N)

【卷积优化】定义转移多项式 gg,其中第 ll 项系数 gl=[2lS]g_{l} = [2 l \neq S],设 f0=1f_{0}=1,答案是 f0×gnf_{0} \times g^n,复杂度 O(K3logN)\mathcal{O}(K^{3}\log N)

CF351C. Jeff and Brackets

构造一个长度为 N×MN \times M 的合法括号序列。第 ii 个位置上的左括号代价为 AimodNA_{i\bmod N},右括号代价为 BimodNB_{i\bmod N}。求最小代价。(1N20,1M1071\leq N\leq 20, 1\leq M\leq 10^7

fi,jf_{i,j} 为到第 ii 的位置、有 jjjNj\leq N)个左括号未被匹配的最小代价。

{fi,j+1fi1,j+AimodNfi,j1fi1,j+BimodN\begin{cases} f_{i, j+1} \gets f_{i-1,j}+A_{i\bmod N} \\ f_{i, j-1} \gets f_{i-1,j}+B_{i\bmod N} \\ \end{cases}

其中 f0,0=0,f0,j=ef_{0,0}=0, f_{0,j}=e。其中 eemin\min 的幺元,即 ++\infty

Ti=[eAieBieAiBieAiBie](N+1)×(N+1)T_i = \begin{bmatrix} e & A_{i} & & \cdots & e \\ B_{i} & e & A_{i} & & \vdots \\ & B_{i} & e & A_{i} \\ \vdots & & B_{i} & \ddots \\ e & \cdots \\ \end{bmatrix}_{(N+1)\times (N+1)}

A0=(0,e,,e)A_0=(0, e, \dots, e),答案为 AMN=A0(T1T2TN)MA_{MN}= A_0 (T_{1}T_{2} \dots T_{N})^M 的第 0 项。

经典 DP

LPS(S)=LCS(S,reverse(S))\text{LPS}(S) = \text{LCS}(S, \text{reverse}(S))

  1. 给定一个长度为 NN 的字符串,为变成回文串,最少需要插入多少字符?
  2. 给定一个长度为 NN,有 KK 种类型的括号的字符串,为变成 RBS,最少需要插入多少字符?(注意区分 K=1K=1K2K \geqslant 2

本质不同的非空回文子序列数量

  • s[i]s[j]s[i] \neq s[j] 时,f[i][j]=f[i+1][j]+f[i][j1]f[i+1][j1]f[i][j] = f[i+1][j] + f[i][j-1] - f[i+1][j-1]
  • s[i]=s[j]s[i] = s[j]
    • 中间没有字符满足 s[x]=s[i]s[x]=s[i]f[i][j]=2×f[i+1][j1]+2f[i][j] = 2 \times f[i+1][j-1] + 2
    • 中间只有一个字符满足 s[x]=s[i]s[x]=s[i]f[i][j]=2×f[i+1][j1]+1f[i][j] = 2 \times f[i+1][j-1] + 1
    • 中间有两个或以上字符满足 s[x]=s[i]s[x]=s[i],记最左最右的分别为 l,rl,rf[i][j]=2×f[i+1][j1]f[l+1][r1]f[i][j] = 2 \times f[i+1][j-1] - f[l+1][r-1]

2150C

给定权值 VV 和排列 A,BA, B

设集合 UU。依次进行 NN 次操作,每次任选一种:

  • 找到当前的最小下标 ii 使得 AiSA_{i} \in S,将 AiA_{i}SS 中移除,将 AiA_{i} 加入 UU
  • 找到当前的最小下标 ii 使得 BiSB_i \in S,将 BiB_iSS 中移除。

求在所有可能的合法操作序列中,最大化 kUVk\sum_{k\in U}V_{k}

方法:DP

充要条件:对于任意一对满足 j<ij < i(即元素 AjA_j 在排列 AA 中出现在 AiA_i 的前面)且 BAj1>BAi1B^{-1}_{A_j} > B^{-1}_{A_i}(即元素 AjA_j 在排列 BB 中出现在 AiA_i 的后面)的下标对 (i,j)(i, j),如果 AiUA_i \in U,那么必须有 AjUA_j \in U

逆否命题:如果 AiUA_i \in U,且 Aj∉UA_j \not \in U,那么它们不能同时满足 j<ij < iBAj1>BAi1B^{-1}_{A_j} > B^{-1}_{A_i}

ii 从小到大进行决策,考虑所有满足 Aj∉UA_j \not \in Ujj,由于天然满足 j<ij < i,则不能满足 BAj1>BAi1B^{-1}_{A_j} > B^{-1}_{A_i},即必须 maxj:Aj∉UBAj1BAi1\max_{j:A_j \not \in U} B^{-1}_{A_j} \leqslant B^{-1}_{A_i}

考虑设 f(i,x)f(i, x) 表示已决策前 ii 个元素,且 x=maxj:Aj∉UBAj1x=\max_{j:A_j \not \in U} B^{-1}_{A_j} 时的最优解。

  • Ai∉UA_{i} \not\in Uf(i,max(x,BAj1))f(i1,x)f(i, \max(x, B^{-1}_{A_j})) \gets f(i - 1, x)
  • AiUA_{i} \in U,当 xBAi1x \leqslant B^{-1}_{A_i} 时,有 f(i,x)f(i1,x)+VAif(i, x) \gets f(i-1, x) + V_{A_i}

初始值:f(0,0)=0f(0, 0) = 0
答案:maxf(N,)\max f(N, *)

用线段树维护 f(x)f(x),转移即区间最值、单点修改、区间加。

1
2
3
4
5
6
7
8
9
10
11
std::vector<Info> init(N + 1);
init[0] = {0};
LazySegmentTree<Info, Lazy> seg(init);
for (int i = 0; i < N; i++) {
int p = invB[A[i]];
auto [M] = seg.query(0, p + 1);
seg.update(0, p + 1, {V[A[i]]});
auto [cur_p] = seg.query(p, p + 1);
seg.modify(p, {std::max(M, cur_p)});
}
auto [ans] = seg.query(0, N + 1);

方法:最大权闭合子图

充要条件:如果选择了 AiUA_i \in U,那么对于所有满足 j<ij < iBAj1>BAi1B^{-1}_{A_j} > B^{-1}_{A_i}jj,必须有 AjUA_j \in U

即选了 AiA_i,就必须选 AjA_j

  1. 所有 Vk>0V_k > 0 的点与源点 SS 相连,容量 VkV_k
  2. 所有 Vk<0V_k < 0 的点与汇点 TT 相连,容量 Vk|V_k|
  3. 如果选 ii 必须选 jj(即 j<ij < iBAj1>BAi1B^{-1}_{A_j} > B^{-1}_{A_i}),则从 iijj 连边,容量 ++\infty

最终的最大收益 =Vk>0Vk= \sum_{V_k > 0} V_k - 最小割,表示满足依赖关系,我们不得不放弃的正权值,或者被迫承受的负权值。

代码的遍历顺序是 ii00N1N-1,这意味着天然满足了 j<ij < i 的条件。当我们处理到第 ii 个元素时,之前遍历过的元素都是候选的 jj

遇到负权点时,相当于在图中增加了一个与汇点 TT 相连的节点。将其容量记录在 std::map 中,键为其在 BB 中的位置 p=BAi1p=B^{-1}_{A_i}。这些记录下来的负权值,就是后续正权点可以用来推流的目标。

遇到正权点时,首先将其乐观地全部加入 max_profit(即公式中的 Vk>0Vk\sum_{V_k > 0} V_k)。接下来,由于它可能依赖于前面遍历过的负权点,我们需要为其寻找能够割掉的代价。

根据条件 BAj1>BAi1B^{-1}_{A_j} > B^{-1}_{A_i},当前正权点只能流向位置大于 p=BAi1p=B^{-1}_{A_i} 的负权点。因此,使用 capacity.lower_bound(p) 查找所有满足条件的负权点。从满足的最小的 BAj1B^{-1}_{A_j} 开始,尝试用当前的 flow 去扣除负权点的容量,并同时从 max_profit 中减去这部分流出的代价。

填加乘

CF2028F. Alice’s Adventures in Addition

在序列中填加号或乘号,使运算结果恰好等于 mm1n2×105, 0ai104, 1m1041 \leqslant n\leqslant 2\times10^{5},\ 0 \leqslant a_{i} \leqslant 10^{4},\ 1 \leqslant m\leqslant 10^{4}

乘法的优先级高,设 dpi,xdp_{i,x} 表示处理到第 ii 个数,

  • 键:上一个加号之后的所有数乘积为 xx

  • 值:上一个加号之前能表示出的所有数的集合,用 bitset 维护。

转移分为 aia_{i} 前面填加号或乘号两种。具体见代码。

暴力转移的复杂度是对的,因为至多 20 个数乘起来就超过 10610^{6} 了,键不会很多,转移量在 logm\log m 级别,每次转移是 bitset 的复杂度 logm\log m。最终时间复杂度 Θ(nlog2m)\Theta(n\log^{2}m),空间复杂度 Θ(log2m)\Theta(\log^{2}m)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
constexpr int N = 1e4 + 8;
map<int, bitset<N>> dp;
dp[a[0]] = 1;
for (int i = 1; i < n; i++) {
map<int, bitset<N>> ndp;
for (auto& [x, mp] : dp) { // 乘号
// if (x * a[i] > m) continue; // WA6 直接跳过而不转移
ndp[min(m + 1, x * a[i])] |= mp;
}
for (auto& [x, mp] : dp) { // 加号
ndp[a[i]] |= mp << x;
}
dp = ndp;
}
bitset<N> res;
for (auto& [x, mp] : dp) {
res |= mp << x;
}
cout << (res[m] ? "YES" : "NO") << endl;

第十届中国大学生程序设计竞赛总决赛 G. + and × with a sugar / + , × 与糖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int n;
cin >> n;
deque<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int sum = 0;
while (!a.empty() && a.back() == 1) {
sum++;
a.pop_back();
}
while (!a.empty() && a.front() == 1) {
sum++;
a.pop_front();
}
bool flag = false;
ll res = 1;
for (int i = 0; i < a.size(); i++) {
res *= a[i];
res %= mod;
if (res > 2 * n) {
flag = true;
}
}
if (n == 0 || flag) {
cout << (res + sum) % mod << endl;
continue;
}
map<int, int> f;
f[a[0]] = 0;
for (int i = 1; i < a.size(); i++) {
map<int, int> nf;
for (auto& [k, v] : f) {
chmax(nf[k * a[i]], v);
chmax(nf[a[i]], v + k);
}
f = nf;
}
int ans = 0;
for (auto& [k, v] : f) {
chmax(ans, v + k);
}
cout << ans + sum << endl;