NOIP 模拟赛:2024-11-6
T1:
给出数组 \(f[1\sim m]\),表示 "长度 \(i\) 的 LIS 的末尾最小元素"(也就是原序列的 LIS 长度为 \(m\))。构造一个长度 \(n\) 值域 \(k\) 的原序列满足 \(f\) 的限制,或判定无解。
一种显然的构造方式:把 \(f\) 数组整个放到原序列最后 \(m\) 个,然后从开头从大往小填。先不断填 \(k\),如果发现再填就会把对应长度的 \(f\) 破坏掉,就开始填 \(k-1\) ……
T2:
给定一棵树,初始有棋子在根结点。两人轮流操作,A 可以把某叶子染黑,B 可以把棋子挪到相邻结点。问最优策略下,B 能否把棋子挪到一个非黑色的结点。
\(dp[i]\) 表示当前棋子在 \(i\),往 \(i\) 的子树里走,A 至少提前染黑多少个叶结点才能保证赢。可以 \(O(n)\) DP。同时注意 \(dp[i]\leftarrow max(dp[i], 0)\)。
T3:
对于一个字符串 \(s\),定义操作 \(f(s,ch)\):在 \(s\) 的每个空里都插入一个 \(ch\)。
给定一个操作序列 \(c_1\sim c_n\),问 \(f(f(\cdots f(\emptyset,c_1),c_2),\cdots)\) 有多少个本质不同的子序列。
反向考虑一种操作 \(f'(s,ch)=s+ch+s\),这样操作等价于 \(c_n\sim c_1\) 做一遍。
令 \(f[x][y]\) 表示当前字符串有多少个子序列以 \(x\) 开头,且末尾加上 \(y\) 之后就不再是子序列。
当两个字符串 \(s_1,s_2\) 合并为 \(s\) 时,其 \(f\) 的变化:\(f_{s}[x][y]=\sum_{k}f_{s_1}[x][k]\cdot f_{s_2}[k][y]\)。(其实就是矩阵乘法的形式)
\(f'\) 可以看作两次字符串合并。
为了方便,给字符集添加一个不存在的字符 #
,\(f[x][27]\) 记录了所有以 \(x\) 开头的子序列个数。
总共 \(2n\) 次合并,一次合并 \(27^3\),总复杂度 \(O(n\times 27^3)\)。
T4:
给定一个 \(2\times n\) 的网格图,第一行的第 \(i\) 个点和第二行的第 \(p_i\) 个点有连边。\(p_i\) 构成一个排列。删掉第 \(i\) 条边的代价为 \(w_i\),同时会把当前所有与它相交的边一起删掉。问删光的最小代价是多少。\(n\le 2\times 10^5\)。
一个观察是,如果最终选择的边是 \(i_1<i_2<\cdots<i_k\),则 \(p_{i_1}<p_{i_2}<\cdots<p_{i_k}\)。同时 \((i_x,i_{x+1})\) 之间 不存在数 \(w\) 使得 \(p_{i_x}<p_w<p_{i_{x+1}}\)。
为了方便,不妨额外建立 \(p_0=0,p_{n+1}=n+1\) 两条无代价的边。
由此导出一个 DP:\(dp[i]\) 表示考虑前 \(i\) 条边全部删光的最小代价,强制 \(i\) 删。转移则枚举 \(j<i\) 为上一条决定删的边。
这个复杂度是 \(O(n^3)\) 的。
分治,先递归进左半边求出左半边的 DP 值。然后考虑左半边某个点作为右半边某个点的前置状态做的贡献。再递归进右半边求出右半边的 DP 值。
问题在于中间的。把左右点取出,各自按照 \(p\) 排序。同时维护左右两个单调栈,左边的单调栈保证 \(i\) 递减,右边的单调栈保证 \(i\) 递增。左边的单调栈保证了都有相交,可以用来更新;右边的单调栈保存了所有可能使得不能更新的可能性,即所有会限制左边点更新的点。
两个指针初始指向左右数组的开头,每次从两个指针中取出 \(p\) 较小的更新:
-
若取出左边的点 \(x\),则在 \(p_x\) 的位置上标记一个 \(dp[x]\)。(在 pop 的时候把对应位置标记为 \(+\infty\))
-
若取出右边的点 \(x\),先 pop,假设此时栈顶的点第一行 \(=y\),则查询 \((p_y,p_x)\) 这个区间内所有标记的最小值,然后把 \(x\) 加入栈。
用一个线段树即可。
复杂度 \(O(n\log^2 n)\)。