题意
给定一个序列 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;
}