1 条题解

  • 0
    @ 2025-8-24 22:07:44

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Mirach
    诶这里还可以填东西?

    搬运于2025-08-24 22:07:44,当前版本为作者最后更新于2019-01-10 20:17:43,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    屠龙宝刀,点击就送,一刀999

    Problem

    题意概要:给定一个长为nn的排列,可以选择一个集合SS使这个集合内部元素排到自己在整个序列中应该在的位置(即对于集合SS内的每一个元素ii,使其排到第ii号位置,使得整个排列在排序后为上升序列。求满足这样条件的,且集合大小最小的集合中字典序第kk小的集合(可能总结不到位,看原题面吧)

    n105n\leq 10^5

    Solution

    不难发现出题人费尽心思写的题面就是在强烈暗示选取一个集合等价于将这个集合内所有元素排到自己该处于的位置(即元素ii应该在位置ii

    进一步发现集合内的元素很自觉的到了正确的位置,而集合外的元素不会更改相对位置,为了使最终整个排列单调递增,即要求集合外的元素必须满足在一开始就是单调递增的

    求字典序第kk小的满足题意的集合,取反一下,就是求序列中字典序第kk大的最长上升子序列

    (至此题目模型转化完成)


    现在目标为求字典序第kk大的最长上升子序列

    在继续之前建议先将最长递增子序列的数量解决:

    设置fif_i表示以权值为ii结尾的LISLIS的长度和数量,则权值xxf1...fx1f_1...f_{x-1}间转移,用树状数组维护前缀最大值和数量即可O(nlogn)O(n\log n)解决


    利用上面这题的思想,已经可以求得以每个元素开头的LISLIS长度和数量

    这题和上面这题虽有不同,但本质类似,想想一般线段树求第 kk 大的过程,正是依次确定每一层的节点,而为了确定每一层的节点,就需要用到所有节点子树和

    同理,假设当前要求的序列的LISLIS长度为 tt,则求第kkLISLIS的一个思想就是先确定第11个数,再在确定第11个数的基础上确定下一个数……以此类推可以最终确定LISLIS的每一位

    细化一下,就是将所有可能作为LISLIS的第ii位的数 放进第ii个vector里,将每个vector内部进行元素排序,在确定每一位时从大到小确定,若当前值后面牵扯的LISLIS数量小于kk,则将kk减去这个数量然后检查下一个值,否则将这个值确定下来并开始确认下一位

    (值得注意的一点,若求LISLISii层选定了位置RR的元素,则接下来都不能选择RR左边的元素)

    Code

    关于代码中的一些疑问:

    • 我没有用vector,而是使用链式前向星替代
    • 由于每个vector里的元素一定是按照位置递增而权值递减的,所以并不需要排序
    • 很多人用线段树,而这题只需要树状数组即可
    • 在求完以每个点开始的LISLIS后树状数组就没用了,可以节约大量时间
    #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
    上传者