题解 P5156 【[USACO18DEC]Sort It Out】
屠龙宝刀,点击就送,一刀999
Problem
题意概要:给定一个长为
Solution
不难发现出题人费尽心思写的题面就是在强烈暗示选取一个集合等价于将这个集合内所有元素排到自己该处于的位置(即元素
进一步发现集合内的元素很自觉的到了正确的位置,而集合外的元素不会更改相对位置,为了使最终整个排列单调递增,即要求集合外的元素必须满足在一开始就是单调递增的
求字典序第
(至此题目模型转化完成)
现在目标为求字典序第
在继续之前建议先将最长递增子序列的数量解决:
设置
利用上面这题的思想,已经可以求得以每个元素开头的
这题和上面这题虽有不同,但本质类似,想想一般线段树求第
同理,假设当前要求的序列的
细化一下,就是将所有可能作为
(值得注意的一点,若求
Code
关于代码中的一些疑问:
- 我没有用vector,而是使用链式前向星替代
- 由于每个vector里的元素一定是按照位置递增而权值递减的,所以并不需要排序
- 很多人用线段树,而这题只需要树状数组即可
- 在求完以每个点开始的
LIS 后树状数组就没用了,可以节约大量时间
#include <bits/stdc++.h>
typedef long long ll;
inline void read(int&x){
char c11=getchar();x=0;while(!isdigit(c11))c11=getchar();
while(isdigit(c11))x=x*10+c11-'0',c11=getchar();
}
const int N=101000;
struct Edge{int v,nxt;}e[N];
int a[N],chs[N],head[N];
int n,_;ll k;
const ll lim=1e18;
struct node{
int v;ll c;
inline node(){}
friend inline void operator + (node&A,const node B){
if(A.v<B.v)A.v=B.v,A.c=B.c;
else if(A.v==B.v)A.c=std::min(lim,A.c+B.c);
}
}d[N],g[N],cl;
#define lb(x) (x&(-x))
inline node qy(int x){node p=cl;for(x;x<=n+1;x+=lb(x))p+d[x];return p;}
inline void add(int x,node y){for(;x;x-=lb(x))d[x]+y;}
inline void ae(int u,int v){e[++_].v=v,e[_].nxt=head[u],head[u]=_;}
int main(){
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;++i)read(a[i]);
cl.c=1,add(n+1,cl),cl.c=0;
for(int i=n;i;--i){
g[i]=qy(a[i]);
++g[i].v;
add(a[i],g[i]);
}
for(int i=n;i;--i)ae(g[i].v,i);
for(int stp=qy(1).v,R=0;stp;--stp)
for(int i=head[stp],v;i;i=e[i].nxt){
v=e[i].v;
if(g[v].c<k)k-=g[v].c;
else {
chs[a[v]]=true;
while(R<v)g[R++]=cl;
break;
}
}
printf("%d\n",n-qy(1).v);
for(int i=1;i<=n;++i)
if(!chs[i])printf("%d\n",i);
return 0;
}