1. 程式人生 > >牛客網暑期ACM多校訓練營(第五場)H subseq(樹狀陣列)

牛客網暑期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;
}