传送门
解析:
不会树状数组进行LIS计数的小伙伴可以先看一下这篇博客:https://blog.csdn.net/zxyoi_dreamer/article/details/86682734
然后,这道题为什么要用LIS计数呢?
首先考虑我们已经选出来一个集合了,我们对这个集合进行各种操作,显然集合里面的元素最终都一定能够到达自己该到的位置。
但是,这个集合外的数的相对位置是显然没有改变过的。。。
换句话说,不管我们怎么选择集合,集合外的数的相对位置不会变化,要做到最后排序成功,必须集合外的数本身就是升序排列。
要求集合元素尽可能少,那么补集元素就要尽可能多。
那么补集是原序列的一个上升序列,同时又要尽可能大,所以就是LIS。
而我们要求原集合的字典序第 K K K小,那么补集的字典序就是所有补集中字典序第 K K K大,显然这是一个对偶问题。
所以我们的问题最终就转化为了求字典序第 K K K大的 L I S LIS LIS。
考虑倒序处理,为每个长度开一个vector,存储有哪些位置开头的LIS长度为 i i i。
然后贪心找出第 K K K大的LIS就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline ll getll(){
re char c;
while(!isdigit(c=gc()));re ll num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
cs ll lim=1e18+5;
cs int N=100005;
int n;ll k;
struct node{
int len;
ll cnt;
void operator+=(cs node &b){
if(len>b.len)return;
if(len<b.len)*this=b;
else cnt=min(cnt+b.cnt,lim);
}
}t[N],f[N];
#define lowbit(x) (x&(-x))
inline void update(int pos,cs node &a){
for(;pos;pos-=lowbit(pos))t[pos]+=a;
}
inline node query(int pos){
node res=(node){0,1};
for(;pos<=n;pos+=lowbit(pos))res+=t[pos];
return res;
}
int a[N];
vector<int> vec[N];
bool vis[N];
signed main(){
n=getint(),k=getll();
for(int re i=1;i<=n;++i)a[i]=getint();
for(int re i=n;i;--i){
f[i]=query(a[i]+1);
++f[i].len;
vec[f[i].len].push_back(i);
update(a[i],f[i]);
}
int m=query(1).len;
for(int re now=m,R=0;now;--now){
for(int re i=vec[now].size()-1;~i;--i){
int pos=vec[now][i];
if(f[pos].cnt<k)k-=f[pos].cnt;
else {
vis[a[pos]]=true;
while(R<pos)f[++R].cnt=0;
break;
}
}
}
cout<<n-m<<"\n";
for(int re i=1;i<=n;++i)if(!vis[i])cout<<i<<"\n";
return 0;
}