Loading

[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\) 结尾,下降子序列末尾元素最大值。转移考虑由哪里转移而来,显然不能暴力转移。

分析性质,上升子序列中相邻两个元素之间必然严格下降的,所以实际上只有个别位置需要转移而来:

  1. \(a_{i-1}<a_i\):从 \(i-1\) 位置转移而来。
  2. 最靠右的 \(f_p>a_{p+1}\) 的位置 \(p\),且 \(a_p<a_i\)\(p\ge b_i\)\(b_i\)\(i-1\) 位置向左一直递增的位置):从 \(p\) 转移而来,\(f\) 值为 \(a_{i-1}\)
  3. \(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;
}
posted @ 2025-04-01 10:06  Pigsyy  阅读(14)  评论(0)    收藏  举报