[2021 牛客国庆day5 H] subseq
https://ac.nowcoder.com/acm/contest/20326/H
题意:
给定\(a\)序列,构造一个第\(k\)大的\(b\)序列,使得\(b\)为范围在\(1\)~\(n\)之间的严格上升子序列,且把\(b\)作为\(a\)的下标,拿出来的子序列也是严格上升子序列。
思路:
倒着做一遍,很容易用树状数组求出以当前\(a_i\)为起点的严格上升子序列的数量。得到这个之后,我们可以从前往后扫一遍,遇到以当前点为起点的子序列数量大于等于\(k\)的情况时,就说明第\(k\)大的子序列在它下面,所以要选进来,同时让\(k\)减\(1\);小于的情况,说明这些都要跳过,直接让\(k\)减掉就行。一路减下去,如果最后\(k\)等于\(0\),说明找到了答案,否则就没有。
但是每次都要把后面的答案都加到当前点的身上,这个成长速度是很快的,\(int128\)也装不下。一个trick是把数组里的值卡在\(1e18\)。但是我们正常来求子序列数量时,要求\(i\) ~ \(tot\)的值,这里存在一个减法,所以不能用这个\(trick\)。那么我们考虑把减法去掉,每次都要求到末尾,那么就把整个树状数组倒过来,最大值放在最前面,就能完美解决这个问题了。
#include
using namespace std;
typedef long long ll;
const ll INF = 1e18 + 1;;
const int N = 5e5 + 7;
void up(ll &a, ll v) {
a += v;
if (a > INF) a = INF;
}
int a[N], b[N], tot;
ll k, ans[N];
int n;
struct Bit {
int n;
ll c[N];
inline int lowbit(int x) {
return x & -x;
}
void init(int sz) {
n = sz;
memset(c, 0, sizeof(ll) * (sz + 1));
}
void add(int x, ll v) {
assert(v >= 0);
while (x <= n) {
up(c[x], v);
x += lowbit(x);
}
}
ll query(int x) {
ll res = 0;
while (x > 0) {
up(res, c[x]);
x -= lowbit(x);
}
return res;
}
}bit;
void solve() {
cin >> n >> k;
for (int i = 1; i<= n; ++i) {
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
tot = unique(b + 1, b + 1 + n) - b - 1;
bit.init(tot);
for (int i = n; i >= 1; --i) {
int id = lower_bound(b + 1, b + 1 + tot, a[i]) - b;
id = tot - id + 1;
bit.add(id, bit.query(id - 1));
bit.add(id, 1);
ans[i] = bit.query(id - 1) + 1;
}
int pre = 0;
vector v;
ll sum = 0;
for (int i = 1; i <= n; ++i) {
up(sum, ans[i]);
}
if (sum < k) {
cout << "-1" << '\n';
return;
}
for (int i = 1; i <= n; ++i) {
if (!k) break;
if (a[i] <= pre) continue;
if (ans[i] < k) {
k -= ans[i];
} else if (ans[i] >= k) {
pre = a[i];
v.emplace_back(i);
k--;
}
}
cout << v.size() << '\n';
for (auto t : v) {
cout << t << ' ';
}
}
int main() {
#ifndef LOCAL
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cout << fixed << setprecision(20);
#endif
int t = 1;
while (t--) solve();
return 0;
}