[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 <bits/stdc++.h>

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<int> 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;
}
posted @ 2021-10-17 11:48  stff577  阅读(137)  评论(0编辑  收藏  举报