【题目】
题目描述:
文翁班上有 n n n 个学生,每次上体育课他们会随机排列为一排,每个人有一个编号 a i a_i ai( 1 1 1 ~ n n n 的排列),现在文翁希望让所有人按编号升序排成一排,可是他不希望采取普通的排序方法来完成。
他开创了一个有趣的排列方法,每一次他会点名一个学生,被点名的学生会确保他的顺序正确,当右边挨着他的人编号比他小,就和右边交换,然后当左边挨着他的人编号比他大他也会交换位置。这样,被点名的学生就完成了按他认为的按顺序排好(在他看来左边的学生编号比他小,右边的学生编号比他大)。
文翁需要选出一个学生编号子集,遍历这个子集,依次叫子集中每个人出列,重复下去直到所有人都排好序。如他选出子集 { 1 , 3 , 6 } \{1,3,6\} {1,3,6},他就会先叫 1 1 1,再叫 3 3 3,再叫 6 6 6。如果这 n n n 个人还没有排好序,他会再次叫 1 , 3 , 6 1,3,6 1,3,6。
很明显这样的子集很多,现在文翁希望得到的子集最小且能完成排序,这样他觉得还不够,他希望得到满足条件的字典序第 k k k 小子集。(保证存在至少 k k k 个符合要求的子集。)
输入格式:
输入的第一行包含两个整数 n n n 和 k k k。含义如题目描述。
第二行包含 n n n 个空格分隔的整数,表示从左到右学生的编号。
输出格式:
第一行输出最少需要选择多少学生数量。
接下来若干行输出在所有可能子集中的字典序 k k k 小的集合编号(从小到大排序后输出)。
样例数据:
【样例 1 1 1】
输入
4 1
4 2 1 3
输出
2
1
4
【样例 2 2 2】
输入
6 1
6 2 4 3 1 5
输出
3
1
3
6
提示:
【样例 1 1 1 解释】
开始的时候序列为 4213 4213 4213 。在文翁叫编号为 1 1 1 的学生之后,序列变为 1423 1423 1423 。在文翁叫编号为 4 4 4 的学生之后,序列变为 1234 1234 1234 。在这个时候,序列已经完成了排序。
【数据规模与约定】
20 % 20\% 20% 的数据, n ≤ 6 n≤6 n≤6,并且 k = 1 k=1 k=1。
另外有 30 % 30\% 30% 的数据, k = 1 k=1 k=1。
100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 5 , 1 ≤ k ≤ 1 0 18 1\le n\le10^5,1≤k≤10^{18} 1≤n≤105,1≤k≤1018
【分析】
这道题有一个前置知识啦:最长上升子序列的数量。
先转换一下题目的意思。
对于题目中一个可行的集合 B,由于我们只对 B 集合里的元素换位置, ∁ A B \complement _AB ∁AB 中元素的相对位置始终没有变,也就是说 ∁ A B \complement _AB ∁AB 需要构成一个上升序列。由于 B 集合要最小,那么 ∁ A B \complement _AB ∁AB 就要最大,就是最长上升子序列。
又因为我们要找出字典序第 k k k 小的 B 集合,那就找出字典序第 k k k 大的最长上升子序列即可。
那么,对于每个点我们都保存两个值, l e n ( i ) len(i) len(i) 表示以 i i i 开头的 LIS 长度, c n t ( i ) cnt(i) cnt(i) 表示以 i i i 开头的 LIS 数量。
然后按照 l e n ( i ) len(i) len(i) 为下标将每个位置 i i i 存进 v e c t o r vector vector 中,即 v e c x vec_x vecx 中保存的是所有长度为 x x x 的 LIS 开头的位置。
我们使用试填法的思想求解。每次,我们选择一个下标最大的位置使得这个位置合法。具体来说,按照 LIS 长度从大到小枚举每一个 v e c t o r vector vector,对于每个 v e c t o r vector vector 从大到小不断的枚举下标直到可用的 LIS 总数大于等于 k k k,我们就可以选取当前枚举到的数来充当 LIS 的这一位。当然,每次填的数必须递增,可以通过跳过比这一位小的数来做到这点。试填完毕后按顺序输出不在 LIS 中数的下标即可。
时间复杂度为 O ( n log 2 n ) O(n\log_2n) O(nlog2n)
【代码】
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
#define lowbit(x) ((x)&(-x))
using namespace std;
int n,a[N];
const ll limit=1e18+5;
struct lis
{
int len;ll cnt;
void operator+=(const lis &b)
{
if(len>b.len) return;
if(len<b.len) *this=b;
else cnt=min(cnt+b.cnt,limit);
}
}f[N],bit[N];
vector<int>vec[N];
bool vis[N];
void add(int i,const lis &x)
{
for(;i;i-=lowbit(i)) bit[i]+=x;
}
lis query(int i)
{
lis ans=(lis){0,1};
for(;i<=n;i+=lowbit(i)) ans+=bit[i];
return ans;
}
int main()
{
long long k;
int i,j,Max,now=0;
scanf("%d%lld",&n,&k);
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
for(i=n;i>=1;--i)
{
f[i]=query(a[i]+1);
f[i].len++,add(a[i],f[i]);
vec[f[i].len].push_back(i);
}
Max=query(1).len;
for(i=Max;i>=1;--i)
{
for(j=vec[i].size()-1;~j;--j)
{
int pos=vec[i][j];
if(f[pos].cnt<k) k-=f[pos].cnt;
else
{
vis[a[pos]]=true;
while(now<pos) f[++now].cnt=0;
break;
}
}
}
printf("%d\n",n-Max);
for(i=1;i<=n;++i)if(!vis[i])printf("%d\n",i);
return 0;
}