校内:传送门
洛谷(原题):传送门
【题目分析】
啊啊啊啊啊啊再给我两分钟~~~~~但好像2min也没法debug完qwq
如果我们选出一个集合A进行点名,那么集合中的元素会朝自己应在的位置前进,最后以达到排序的目的,但我们发现集合外元素的相对位置是不会变的,也就是说集合外的元素会构成一个上升子序列。
记一个结论:k小集合对应k大补集。
所以这道题就转化为求序列第k大上升子序列。
求k大上升子序列的方法可以参考这篇博客:传送门
总之就是用树状数组去维护其前缀比当前点小的状态并更新比当前点大的状态。
求k大就贪心,枚举上升序列长度(最长即为最长上升子序列长度),如果数量大于等于k就直接更新答案退出,否则减去当前长度的上升序列数量继续枚举。
思路很妙,可以用于上升序列的计数问题。
【代码~】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=1e5+10;
int n,cnt=1;
LL k,maxx=1e18;
int a[MAXN],in[MAXN];
int head[MAXN];
int nxt[MAXN],to[MAXN];
struct Tree{
int v;
LL c;
friend inline Tree operator+(const Tree &a,const Tree &b){
Tree c;
if(a.v<b.v)
c=b;
else{
if(a.v==b.v)
c.v=a.v,c.c=min(maxx,a.c+b.c);
else
c.v=a.v,c.c=a.c;
}
return c;
}
}tr1[MAXN],tr2[MAXN],zer;
LL Read(){
LL i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
void add(int x,int y){
nxt[cnt]=head[x];
head[x]=cnt;
to[cnt]=y;
cnt++;
}
int lowbit(int x){
return x&(-x);
}
Tree query(int x){
Tree ret=zer;
for(x;x<=n+1;x+=lowbit(x))
ret=ret+tr1[x];
return ret;
}
void merge(int x,Tree y){
for(;x;x-=lowbit(x))
tr1[x]=tr1[x]+y;
}
int main(){
n=Read(),k=Read();
for(int i=1;i<=n;++i)
a[i]=Read();
zer.c=1,merge(n+1,zer),zer.c=0;
for(int i=n;i;--i){
tr2[i]=query(a[i]);
++tr2[i].v;
merge(a[i],tr2[i]);
}
for(int i=n;i;--i){
add(tr2[i].v,i);
}
for(int u=query(1).v,x=0;u;--u){
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(tr2[v].c<k)
k-=tr2[v].c;
else{
in[a[v]]=1;
while(x<v)
tr2[x++]=zer;
break;
}
}
}
cout<<n-query(1).v<<'\n';
for(int i=1;i<=n;++i)
if(!in[i])
cout<<i<<'\n';
return 0;
}