LG P5156 [USACO18DEC]Sort It Out dp+树状数组

题面

洛谷传送门
USACO传送门

解法

  • 结论应该比较显然,就是相当于找字典序第 K K 大的 L I S LIS
  • 考虑怎么证明其正确性。对于一次 s o r t ( x ) sort(x) 操作,可以发现除 x x 之外的元素的相对位置并不发生改变。那么,现在相当于要找一个最大的集合 S S ,使得它们在序列中严格递增。显然 S |S| 等于 L I S LIS 的长度。然后再考虑字典序的问题。最后答案的集合 S S' S S 的补集,那么求字典序第 K K 小的集合 S S' 就相当于求字典序第 K K 大的集合 S S
  • 那么问题就是怎么求最后的 S S
  • 考虑到字典序应该是从第一位开始按位确定的,所以我们可以设 f [ i ] f[i] 表示以 a [ i ] a[i] 开头的 L I S LIS 的长度, s [ i ] s[i] 表示以 a [ i ] a[i] 开头的 L I S LIS 的个数。
  • 转移比较简单,用树状数组即可。
  • 然后不妨将 f [ i ] f[i] 相同的全部放进一个vector里,那么显然的是,在同一个vector中有两个数 x , y x,y 满足 x > y x>y ,那么 a [ x ] < a [ y ] a[x]<a[y] ,否则 f [ y ] f[y] 一定会更新。那么说明在编号 x x 有序的情况下 a [ x ] a[x] 也同样有序。
  • 然后直接按位确定即可。
  • 时间复杂度: O ( n log n ) O(n\log n)

【注意事项】

  • 一开始的思路有些问题,认为倒着确定也同样满足字典序的条件。其实这个显然是错误的,这个结论只能满足于 K = 1 K=1 的情况。
  • 注意 K K 最大只有 1 0 18 10^{18} ,但是 s [ i ] s[i] 很有可能会超过 K K ,所以在求 s [ i ] s[i] 的时候要与 1 0 18 10^{18} 取最小值,否则会变成负数出现问题。(因为这个问题调了好久)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 100010; const ll inf = 1e18l;
int n, a[N], st[N], used[N]; ll K;
vector <ll> v[N], cnt[N];
struct BIT {
	ll f[N];
	void modify(int x, ll v) {for (; x <= n; x += x & -x) f[x] = min(f[x] + v, inf);}
	void Clear(int x) {for (; x <= n; x += x & -x) f[x] = 0;}
	ll query(int x) {
		ll ret = 0;
		for (; x; x -= x & -x) ret = min(inf, ret + f[x]);
		return ret;
	}
} T;
int main() {
	read(n), read(K);
	for (int i = 1; i <= n; i++) read(a[i]);
	int len = 0;
	for (int i = n; i; i--) {
		int l = 1, r = len, ans = 0;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (st[mid] < a[i]) l = mid + 1, ans = mid;
				else r = mid - 1;
		}
		v[ans + 1].push_back(i);
		if (ans == len) st[++len] = a[i];
			else st[ans + 1] = a[i];
	}
	for (int i = 0; i < v[1].size(); i++) cnt[1].push_back(1);
	for (int i = 2; i <= len; i++) {
		int p = 0;
		for (int j = 0; j < v[i].size(); j++) {
			int k = v[i][j];
			while (p < v[i - 1].size() && v[i - 1][p] > k)
				T.modify(n - a[v[i - 1][p]] + 1, cnt[i - 1][p]), p++;
			cnt[i].push_back(T.query(n - a[k] + 1));
		}
		for (int j = 0; j < v[i - 1].size(); j++) T.Clear(n - a[v[i - 1][j]] + 1);
	}
	cout << n - len << "\n";
	for (int i = len, x = 0; i; i--)
		for (int j = 0; j < v[i].size(); j++) {
			int k = v[i][j];
			if (k < x || a[k] < a[x]) continue;
			if (cnt[i][j] < K) {K -= cnt[i][j]; continue;}
			used[a[k]] = 1, x = k; break;
		}
	for (int i = 1; i <= n; i++) if (!used[i]) cout << i << "\n";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/87925559