1 条题解
-
0
自动搬运
来自洛谷,原作者为

Mirach
诶这里还可以填东西?搬运于
2025-08-24 22:07:44,当前版本为作者最后更新于2019-01-10 20:17:43,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Problem
题意概要:给定一个长为的排列,可以选择一个集合使这个集合内部元素排到自己在整个序列中应该在的位置(即对于集合内的每一个元素,使其排到第号位置,使得整个排列在排序后为上升序列。求满足这样条件的,且集合大小最小的集合中字典序第小的集合(可能总结不到位,看原题面吧)
Solution
不难发现出题人费尽心思写的题面就是在强烈暗示选取一个集合等价于将这个集合内所有元素排到自己该处于的位置(即元素应该在位置)
进一步发现集合内的元素很自觉的到了正确的位置,而集合外的元素不会更改相对位置,为了使最终整个排列单调递增,即要求集合外的元素必须满足在一开始就是单调递增的
求字典序第小的满足题意的集合,取反一下,就是求序列中字典序第大的最长上升子序列
(至此题目模型转化完成)
现在目标为求字典序第大的最长上升子序列
在继续之前建议先将最长递增子序列的数量解决:
设置表示以权值为结尾的的长度和数量,则权值从间转移,用树状数组维护前缀最大值和数量即可解决
利用上面这题的思想,已经可以求得以每个元素开头的长度和数量
这题和上面这题虽有不同,但本质类似,想想一般线段树求第 大的过程,正是依次确定每一层的节点,而为了确定每一层的节点,就需要用到所有节点子树和
同理,假设当前要求的序列的长度为 ,则求第大的一个思想就是先确定第个数,再在确定第个数的基础上确定下一个数……以此类推可以最终确定的每一位
细化一下,就是将所有可能作为的第位的数 放进第个vector里,将每个vector内部进行元素排序,在确定每一位时从大到小确定,若当前值后面牵扯的数量小于,则将减去这个数量然后检查下一个值,否则将这个值确定下来并开始确认下一位
(值得注意的一点,若求第层选定了位置的元素,则接下来都不能选择左边的元素)
Code
关于代码中的一些疑问:
- 我没有用vector,而是使用链式前向星替代
- 由于每个vector里的元素一定是按照位置递增而权值递减的,所以并不需要排序
- 很多人用线段树,而这题只需要树状数组即可
- 在求完以每个点开始的后树状数组就没用了,可以节约大量时间
#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; }
- 1
信息
- ID
- 4135
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者