1 条题解
-
0
题意:给一个排列 ,定义其中一个子集是合法的,当且仅当可以对子集内元素进行若干次操作,使得排列有序。对 操作,先将其拎出来然后从左往右比较直到遇见比它大元素,然后插入。求字典序第 小的大小最小合法子集。。
合法的必要条件是剩下元素有序(因为无法改变它们内部顺序),容易发现这是充要的。貌似不好比较因为还要排序,注意到剩下元素的有序性,考虑通过它来比较。发现两个子集 当且仅当 。
求第 大的最长上升子序列是容易的,预处理一下每个起点的最长上升子序列长度和方案。。
代码
#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
- 上传者