2018牛客多校第五场 H.subseq

题意:

  给出a数组的排列。求出字典序第k小的b数组的排列,满足1<=bi<=n,bi<bi+1,a[b[i]]<a[b[i+1]],m>0。

题解:

  用树状数组倒着求出以每个数为首的递增子序列个数。若总的个数之和小于k则输出-1。

  总的个数可能非常大而k<=1e18。所以要判下上界。

  最后从1~n扫一遍。当前数大于上一个加入答案的数时,若以它为首的递增子序列个数小于k,则用k减去那个个数,否则将这个数加入答案并将k-1(即减去后面不再加数的情况)。

  k = 0时跳出循环。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5+10;
const ll inf = 1e18+10;
int n, tot, pre;
ll k, sum;
int a[N], id[N], ans[N];
ll tre[N], val[N];
ll add(ll x, ll y) { return min(x + y, inf); }
void update(int pos, ll val) {
    while(pos > 0) {
        tre[pos] = add(tre[pos], val);
        pos -= pos&(-pos);
    }
}
ll query(int pos) {
    ll res = 0;
    while(pos <= n) {
        res = add(res, tre[pos]);
        pos += pos&(-pos);
    }
    return res;
}
int main() {
    scanf("%d%lld", &n, &k);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        id[i] = a[i];
    }
    sort(id+1, id+n+1);
    for(int i = n; i >= 1; i--) {
        int p = lower_bound(id+1, id+n+1, a[i])-id;
        val[i] = query(p+1)+1;
        update(p, val[i]);
    }
    for(int i = 1; i <= n; i++) sum = add(sum, val[i]);
    if(sum < k) {
        puts("-1");
        return 0;
    }
    for(int i = 1; i <= n; i++) {
        if(k == 0) break;
        if(a[i] <= a[pre]) continue;
        if(val[i] < k) k -= val[i];
        else {
            ans[++tot] = i;
            k--;
            pre = i;
        }
    }
    printf("%d\n", tot);
    for(int i = 1; i < tot; i++) printf("%d ", ans[i]);
    printf("%d\n", ans[tot]);
}
View Code

猜你喜欢

转载自www.cnblogs.com/Pneuis/p/9420437.html