T2 最长上升子序列(lis)题解
T2 最长上升子序列(lis)题解
题目大意
给定 \(1 \sim n\) 的排列 \(a\)。对每个 \(i = 1, 2, \dots, n-1\),交换 \(a_i\) 与 \(a_{i+1}\),求交换后的最长严格上升子序列(LIS)长度。各次交换相互独立,每次都从原排列开始。
思路
核心观察:相邻交换后,LIS 长度只能是 \(L-1\)、\(L\) 或 \(L+1\)(\(L\) 为原 LIS 长度)。因为交换只影响同时包含这两个元素的子序列,删去其中一个就能恢复原排列的子序列。
分两种情况讨论:
情况一:\(a_i < a_{i+1}\)(递增对)
交换后不会产生新的上升子序列,只会破坏同时选择这两个元素的原子序列。
- 如果两个位置都是必经位置(每条 LIS 都必须经过它们),那么所有原 LIS 都被破坏,答案为 \(L-1\)
- 否则,存在一条 LIS 不包含其中至少一个元素,这条 LIS 保留,答案仍为 \(L\)
情况二:\(a_i > a_{i+1}\)(递减对)
原 LIS 全部保留,答案不会减少。新增的上升子序列必须同时选择交换后的两个元素(\(y = a_{i+1}\) 在前,\(x = a_i\) 在后)。
设 \(f_i\) 为以 \(i\) 结尾的 LIS 长度,\(g_i\) 为以 \(i\) 开头的 LIS 长度,则:
\(ans_i = \max(L,\ f_{i+1} + g_i)\)
左侧选值 \(< y\) 的最长上升链(即 \(f_{i+1}\)),右侧选值 \(> x\) 的最长上升链(即 \(g_i\)),两部分可以直接拼接。
必经位置的判定
位置 \(i\) 属于某条 LIS 的充要条件:\(f_i + g_i - 1 = L\)。
按 \(f_i\) 分层,记第 \(k\) 层有多少个这样的候选。必经位置满足:
- \(f_i + g_i - 1 = L\)(在某条 LIS 上)
- 且 \(cnt[f_i] = 1\)(这一层只有它一个候选,每条 LIS 都必须选它)
AC code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 200000 + 5;
int n;
int a[N], f[N], g[N];
int stk[N], top;
int cnt[N];
bool necessary[N];
int ans[N];
int main () {
freopen("lis.in", "r", stdin);
freopen("lis.out", "w", stdout);
ios::sync_with_stdio (false);
cin.tie (0);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
top = 0;
for (int i = 1; i <= n; i ++) {
int k = lower_bound (stk + 1, stk + top + 1, a[i]) - stk;
stk[k] = a[i];
if (k > top) top = k;
f[i] = k;
}
int L = top;
top = 0;
for (int i = n; i >= 1; i --) {
int v = n + 1 - a[i];
int k = lower_bound (stk + 1, stk + top + 1, v) - stk;
stk[k] = v;
if (k > top) top = k;
g[i] = k;
}
for (int i = 1; i <= n; i ++)
if (f[i] + g[i] - 1 == L)
cnt[f[i]] ++;
for (int i = 1; i <= n; i ++)
necessary[i] = (f[i] + g[i] - 1 == L && cnt[f[i]] == 1);
for (int i = 1; i < n; i ++) {
if (a[i] < a[i + 1]) ans[i] = L - (necessary[i] && necessary[i + 1]);
else ans[i] = max (L, f[i + 1] + g[i]);
}
for (int i = 1; i < n; i ++) cout << ans[i] << ' ';
return 0;
}
晨雾纸间
山中何事?松花酿酒,春水煎茶
文/lvwangshu·书于云深处
晨雾纸间
✦
原创·转载请注明出处
✦
浙公网安备 33010602011771号