洛谷 P7931

以下标为横坐标,值为纵坐标,建立坐标系。
然后会发现每个点的后继在其右上方。
按照每个点 LIS 大小来分层,以样例 \(3\) 为例:

注意到同层之间一定满足 \(x\) 递增 \(y\) 递减。

结论:存在一种选择 LIS 的最优方案,满足每个 LIS 在图上不交叉。
如样例 \(3\) 选的两组 LIS 就是 \(A,C,E\)\(B,F,G\),显然没有交叉。
证明:以这种情况为例,

如果 \(A\) 选了 \(D\),但是 \(B\) 不能选 \(C\)
如果 \(A\) 选了 \(C\)\(B\) 也能选 \(D\)

Code:

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define pp pop_back
const int N = 1000005;
int n;
int a[N];
int dp[N], len;
vector <int> vec[N], tmp;
vector <vector <int> > ans;

void solve() {
	while (1) {
		if (tmp.empty())
			if (vec[1].empty()) break;
			else tmp.pb(vec[1].back()), vec[1].pp();
		else if (tmp.size() == len) ans.pb(tmp), tmp.clear();
		else {
			int k = tmp.size() + 1, cur = tmp.back();
			while (vec[k].size() && vec[k].back() < cur) vec[k].pp();
			if (vec[k].empty() || a[cur] > a[vec[k].back()]) tmp.pp();
			else tmp.pb(vec[k].back()), vec[k].pp();
		}
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i) {
		if (dp[len] < a[i]) { dp[++len] = a[i], vec[len].pb(i); }
		else {
			int p = lower_bound(dp + 1, dp + len + 1, a[i]) - dp;
			dp[p] = a[i], vec[p].pb(i);
		}
	}
	for (int i = 1; i <= n; ++i) reverse(vec[i].begin(), vec[i].end());
	solve();
	printf("%d %d\n", ans.size(), len);
	for (auto i : ans) {
		for (auto x : i) printf("%d ", x);
		printf("\n");
	}
	return 0;
}
posted @ 2022-10-04 19:52  Kobe303  阅读(28)  评论(0编辑  收藏  举报