CF1530H Turing's Award

参考官方题解。

你发现这个覆盖不太好考虑,考虑时间倒流,变成如下形式:

一开始,小 A 的位置上有一个数 \(a_n\),然后对于接下来 \(n-1\) 步,每次小 A 可以向左走/向右走/不动,然后如果此时小 A 所站的位置上没有数,就写上 \(a_i\),求最后形成序列的最长上升子序列长度。

考虑到任意时刻的序列一定是包含原点的连续序列 \(b\),并且此时不能覆盖有数的位置,那么 \(a_i\) 只可能插入到 \(b\) 的首尾两个位置。

我们将所有最终\(b\) 中的元素 \(a_i\) 成为「好」的元素,显然 \(a_n\) 一定是好元素,观察到:

  1. 我们并不关心小 A 走到 \(b\) 中间哪个位置,只关心他是否有时间在已经确定的连续好元素之间移动。例如,目前考虑了 \(a_i,a_{i+1},\cdots, a_{n}\),钦定 \(a_i\) 为好元素并且插入到 \(b\)首位(插入末尾的情况是对称的),目前 \(a_i,a_{i+1},\cdots ,a_n\) 中一共有 \(k\) 个好元素,考虑第 \(k+1\) 个好元素 \(a_j(j<i)\)
  • 如果 \(a_j\) 插入在 \(b\)首位,我们可以在小 A 走过 \(i-1,i-2,\cdots,j+1\) 的时候都选择留在原位,这样保证不会覆盖,然后 \(j\) 时刻再向左移动一位。所以这种情况没有限制。
  • 如果 \(a_j\) 插入在 \(b\)末尾,小 A 要走过 \(k\) 个好元素。那么需要满足 \(i-j\ge k\)
  1. 考虑一个好元素 \(a_j\)\(j\neq n\))如果存在一种方案使得 \(a_j\) 在最终的 \(b\) 序列里不在 LIS 中,一定存在一种方案使得 \(a_j\) 最终不在 \(b\) 序列中,因为可以在时刻 \(j\) 留在原地不动。所以我们更改好元素的定义:最终出现在 LIS 中的元素(包括 \(a_n\))。

然后我们可以讨论 \(a_n\) 是否属于 LIS,考虑 dp:

\(f_L(k,i)\) 表示当前从 \(n\) 考虑到 \(i\)\(a_i\) 插入到了 LIS 的首位,目前一共有 \(k\) 个好元素,LIS 末尾元素的最小值\(f_R(k,i)\) 表示当前从 \(n\) 考虑到 \(i\)\(a_i\) 插入到了 LIS 的末尾,目前一共有 \(k\) 个好元素,LIS 首位元素的最大值。那么我们要求的就是最大的 \(k\),使得存在一个 \(i\),状态 \(f_{L}(k,i)\) 或者 \(f_{R}(k,i)\) 合法(属于 \([1,n]\))。

根据 \(a_n\) 是否属于 LIS,我们可以确定 dp 的初始状态。

考虑 \(f_{L}(k,i)\) 可以贡献到的状态(\(f_R(k,i)\) 类似),讨论新的好元素 \(a_j\) 放左还是放右即可:

  • \(f_{L}(k,i)\to f_{L}(k+1,j)\quad j<i\land a_j<a_i\)
  • \(a_i\to f_{R}(k+1,j)\quad \quad \quad \ j<i-k\land a_j>f_{L}(k,i)\)

那么不难得到 \(f_{L},f_{R}\) 的转移:

\[\begin{aligned}f_{L}(k+1,i)=\min\left(\min\limits_{j>i\land a_j>a_i}f_{L}(k,j),\min\limits_{j\ge i+k\land f_R(k,j)\ge a_i}a_j\right)\\f_{R}(k+1,i)=\max\left(\max\limits_{j>i\land a_j<a_i}f_{R}(k,j),\max\limits_{j\ge i+k\land f_L(k,j)\le a_i}a_j\right)\end{aligned} \]

显然可以优化,用两个数据结构支持查询前缀 \(\max\) 和后缀 \(\min\) 即可。复杂度 \(O(kn\log n)\)\(k\) 为答案,考虑到均匀随机序列的 LIS 长度期望\(O(\sqrt n)\)\(k\)\(O(\sqrt n)\),复杂度 \(O(n\sqrt n\log n)\)

其实这里有一个简易的 dp 优化套路:将 dp 答案与状态的一维交换,例如我们也可以按照 LIS 的首尾位为状态 dp,但是复杂度更劣。所以把 LIS 长度列入 dp 状态。

写起来很简单,而且跑得巨快无比。但是为什么 *3400 评紫呢,可能是因为数据结构优化 dp 对于卷怪们已经是套路了吧。

const int N = 1.5e4 + 150;
const int inf = 0x3f3f3f3f;
int T, n, ans, a[N], fl[2][N], fr[2][N], tl[N], tr[N];

#define lb(x) (x & (-x))
void updmn(int x, int y) { if (x <= 0 || x > n) return; for (int i = x; i; i -= lb(i)) tl[i] = min(tl[i], y); }
int qrymn(int x) { int res = inf; for (int i = x; i <= n; i += lb(i)) res = min(res, tl[i]); return res; }
void updmx(int x, int y) { if (x <= 0 || x > n) return; for(int i = x; i <= n; i += lb(i)) tr[i] = max(tr[i], y); }
int qrymx(int x) { int res = -inf; for (int i = x; i; i -= lb(i)) res = max(res, tr[i]); return res; }

int dp(int p) {
	int t = 0;
	for (int len = p; ; len++, t ^= 1) {
		int flg = 0;
		for (int i = 1; i <= n; i++) {
			if (fl[t][i] <= n || fr[t][i] >= 1) {
				flg = 1;
				break;
			}
		}
		if (!flg) return len - p;
		memset(tl, inf, sizeof(int) * (n + 10));
		memset(tr, -inf, sizeof(int) * (n + 10));
		for (int i = n; i; i--) {
			if (i < n) updmn(a[i + 1], fl[t][i + 1]), updmx(a[i + 1], fr[t][i + 1]);
			if (i + len <= n) updmn(fr[t][i + len], a[i + len]), updmx(fl[t][i + len], a[i + len]);
			fl[t ^ 1][i] = qrymn(a[i]), fr[t ^ 1][i] = qrymx(a[i]);
		}
	}
}

void solve() {
	n = rd(), ans = 0;
	for (int i = 1; i <= n; i++)
		a[i] = rd(), fl[0][i] = fr[0][i] = a[i];
	ans = max(ans, dp(2));
	memset(fl[0], inf, sizeof(int) * (n + 10));
	memset(fr[0], -inf, sizeof(int) * (n + 10));
	fl[0][n] = fr[0][n] = a[n];
	ans = max(ans, dp(1));
	wr(ans), puts("");
}

int main() {
	T = rd();
	while (T--) solve();
	return 0;
}
posted @ 2023-07-20 18:25  Ender_32k  阅读(5)  评论(0编辑  收藏  举报