牛客网暑期ACM多校训练营(第五场)H subseq(树状数组)

题意

给定一个序列 a[1..n],求下标字典序第 k 小的严格递增子序列

题解

考虑逐位确定,每次计算 a[i…n] 中,以a[i]这个数字为开头的严格递增子序列的个数,用树状数组统计,然后1…n与k比较,小于k就减去dp[i],否则就放a[i],当然要保证a[i]大于前一个放的数;这个树状数组和以前的不太一样,是记录大于等于x的数量,与一般的是倒过来的,虽然有些意外不过想了想确实是,以前的是记在区间的右端点,现在是记录在左端点

代码

#include<bits/stdc++.h>
#define N 1000005
#define P pair<int,int>
using namespace std;
typedef long long ll;
const int M=1e9+7;
const int inf=1e9+7;
ll c[N],dp[N];
int b[N],ans[N],a[N];
void add(int x,ll k,int n)
{
    while(x){
        c[x]+=k;
        if(c[x]>1e18)c[x]=1e18;
        x-=(x&-x);
    }
}
ll sum(int x,int n)
{
    ll ans=0;
    while(x<=n){
        ans+=c[x];
        x+=(x&-x);
        if(ans>1e18)ans=1e18;
    }
    return ans;
}
int main()
{
    int n;
    ll k;
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    int tot=unique(b+1,b+n+1)-b;
    for(int i=n;i;i--){
        a[i]=lower_bound(b+1,b+tot,a[i])-b;
        ll tmp=sum(a[i]+1,tot);
        dp[i]=tmp+1;
        add(a[i],tmp+1,tot);
    }
    int p=0;
    for(int i=1;i<=n&&k;i++){
        if(p&&a[i]<=a[ans[p]])continue;
        if(k>dp[i])k-=dp[i];
        else ans[++p]=i,k--;
    }
    if(!p||k)printf("-1\n");
    else {
        printf("%d\n%d",p,ans[1]);
        for(int i=2;i<=p;i++)
            printf(" %d",ans[i]);
        puts("");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/abutoto/article/details/81385901
今日推荐