[URAL1965] Pear Trees
题意
给定长度为 \(n\) 的序列 \(a\),其由两个单调子序列构成,输出这两个序列或报告无解。
数据范围:\(1\le n\le 10^5\)。
思路
Case 1
对于两个上升 / 下降子序列构成的序列,相当于要求划分成最少的上升序列,这是经典问题。考虑贪心求解,维护两个上升序列序列 \(A,B\),其中 \(A\) 的末尾元素严格大于 \(B\) 的末尾元素。
当加入元素 \(a_i\) 时,若能加入 \(A\) 中则加入,因为这样两个序列的末尾元素达到最优(若加入 \(B\) 中,\(A\) 的末尾元素大于 \(B\) 的末尾元素,显然不优)。
综上,这样求解出来的 \(A,B\) 序列便是数量最少的上升子序列。
Case 2
对于一个上升子序列、一个下降子序列构成的序列,考虑 DP 求解。令 \(f_i\) 表示上升子序列以 \(i\) 结尾,下降子序列末尾元素最大值。转移考虑由哪里转移而来,显然不能暴力转移。
分析性质,上升子序列中相邻两个元素之间必然严格下降的,所以实际上只有个别位置需要转移而来:
- \(a_{i-1}<a_i\):从 \(i-1\) 位置转移而来。
- 最靠右的 \(f_p>a_{p+1}\) 的位置 \(p\),且 \(a_p<a_i\) 且 \(p\ge b_i\)(\(b_i\) 为 \(i-1\) 位置向左一直递增的位置):从 \(p\) 转移而来,\(f\) 值为 \(a_{i-1}\)。
- \(f_{b_i-1}>a_{b_i}\):从 \(b_i-1\) 转移而来,\(f\) 值为 \(a_{i-1}\)。
时间复杂度:\(O(n)\)。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
using i64 = long long;
using pii = pair<int, int>;
const int maxn = 100005;
int n;
int a[maxn], b[maxn];
int f[maxn], g[maxn], vis[maxn];
int main() {
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i ++)
cin >> a[i];
for (int i = n; i >= 1; i --) {
int j = i - 1;
while (j > 1 && a[j - 1] > a[j]) j --;
for (int k = j + 1; k <= i; k ++)
b[k] = j;
i = j + 1;
}
memset(f, -1, sizeof f);
memset(g, 0, sizeof g);
int lst = 0;
a[0] = 1 << 30;
for (int i = 1; i <= n; i ++) {
if (b[i] <= 1) f[i] = a[i - 1], g[i] = 0;
if (a[i - 1] < a[i]) f[i] = f[i - 1], g[i] = i - 1;
if (lst < i && a[lst] < a[i] && lst >= b[i])
if (a[i - 1] > f[i]) f[i] = a[i - 1], g[i] = lst;
if (a[b[i] - 1] < a[i] && f[b[i] - 1] > a[b[i]])
if (a[i - 1] > f[i]) f[i] = a[i - 1], g[i] = b[i] - 1;
if (f[i - 1] > a[i]) lst = i - 1;
}
int u = -1;
if (~f[n]) u = n;
if (~f[n - 1] && f[n - 1] > a[n]) u = n - 1;
for (int i = n; i >= 3; i --)
if (a[i - 1] < a[i]) break;
else if (~f[i - 2] && f[i - 2] > a[i - 1]) {
u = i - 2;
break;
}
if (~u) {
std::vector<int> R1, R2;
while (u) vis[u] = 1, u = g[u];
for (int i = 1; i <= n; i ++)
if (vis[i]) R1.push_back(abs(a[i]));
else R2.push_back(abs(a[i]));
cout << R1.size() << " " << R2.size() << endl;
for (auto v : R1) cout << v << " ";
cout << endl;
for (auto v : R2) cout << v << " ";
cout << endl;
return 0;
}
for (int t : {0, 1}) {
std::vector<int> R1, R2;
bool ok = false;
for (int i = 1; i <= n; i ++)
if (!R1.size() || R1.back() < a[i]) R1.push_back(a[i]);
else if (!R2.size() || R2.back() < a[i]) R2.push_back(a[i]);
else {
ok = true;
break;
}
if (!ok) {
if (!R2.size()) R2.push_back(R1.back()), R1.pop_back();
cout << R1.size() << " " << R2.size() << endl;
for (auto v : R1) cout << abs(v) << " ";
cout << endl;
for (auto v : R2) cout << abs(v) << " ";
cout << endl;
return 0;
}
for (int i = 1; i <= n; i ++)
a[i] = -a[i];
}
cout << "Fail" << endl;
return 0;
}

浙公网安备 33010602011771号