1 条题解

  • 0
    @ 2026-5-7 23:09:22

    题意:给一个排列 a1ana_1\dots a_n,定义其中一个子集是合法的,当且仅当可以对子集内元素进行若干次操作,使得排列有序。对 ai=xa_i=x 操作,先将其拎出来然后从左往右比较直到遇见比它大元素,然后插入。求字典序第 kk 小的大小最小合法子集。n105,k1018n\le 10^5,k\le 10^{18}

    合法的必要条件是剩下元素有序(因为无法改变它们内部顺序),容易发现这是充要的。貌似不好比较因为还要排序,注意到剩下元素的有序性,考虑通过它来比较。发现两个子集 A<BA<B 当且仅当 A>B\complement A>\complement B

    求第 kk 大的最长上升子序列是容易的,预处理一下每个起点的最长上升子序列长度和方案。O(nlogn)O(n\log n)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    
    #define int long long
    const int N = 1e5 + 5;
    int n, k, a[N], rk[N], lis;  
    struct node{int mx, cnt;} f[N];
    bool vis[N];
    vector<int> vc[N];
    
    inline void ADD(node &A, node B){
    	if(A.mx < B.mx) A = {B.mx, 0};
    	if(A.mx == B.mx) A.cnt = min(A.cnt + B.cnt, k);
    }
    namespace BIT{
    	node t[N];
    	inline void upd(int a, node k) {for(; a < N; a += a & -a) ADD(t[a], k);}
    	inline node qry(int a) {node res = {0, 0}; for(; a > 0; a -= a & -a) ADD(res, t[a]); return res;}
    }using namespace BIT;
    signed main(){
    	cin >> n >> k;
    	for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]), rk[a[i]] = i;	
    	for(int i = n; i; --i){
    		f[i] = qry(n - a[i] + 1);
    		if(f[i].cnt == 0) f[i] = {1, 1}; 
    		else ++f[i].mx;
    		upd(n - a[i] + 1, f[i]);
    		lis = max(lis, f[i].mx);
    		vc[f[i].mx].push_back(a[i]);
    	}
    	for(int i = 1; i <= n; ++i) sort(vc[i].begin(), vc[i].end(), [](int a, int b){return a > b;});
    	printf("%lld\n", n - lis);
    	for(int len = lis, lst = 0; len; --len){
    		for(auto i : vc[len]){
    			if(lst > rk[i] || a[lst] > i) continue;
    			if(f[rk[i]].cnt >= k){
    				lst = rk[i], vis[i] = true;
    				break;
    			}
    			else k -= f[rk[i]].cnt;
    		}
    	}
    	for(int i = 1; i <= n; ++i)	if(!vis[i]) printf("%lld\n", i);
    	return 0;
    }
    
    • 1

    信息

    ID
    6785
    时间
    2000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    7
    已通过
    3
    上传者