题目大意
FJ有 N(1≤N≤1e5 )头奶牛(分别用 1…N 编号)排成一行。FJ喜欢他的奶牛以升序排列,不幸的是现在她们的顺序被打乱了。在过去FJ曾经使用一些诸如“冒泡排序”的开创性的算法来使他的奶牛排好序,但今天他想偷个懒。取而代之,他会每次对着一头奶牛叫道“按顺序排好”。当一头奶牛被叫到的时候,她会确保自己在队伍中的顺序是正确的(从她的角度看来)。当有一头紧接在她右边的奶牛的编号比她小,她们就交换位置。然后,当有一头紧接在她左边的奶牛的编号比她大,她们就交换位置。这样这头奶牛就完成了“按顺序排好”,在这头奶牛看来左边的奶牛编号比她小,右边的奶牛编号比她大。
FJ想要选出这些奶牛的一个子集,然后遍历这个子集,依次对着每一头奶牛发号施令(按编号递增的顺序),重复这样直到所有N头奶牛排好顺序。例如,如果她选出了编号为 {2,4,5} 的奶牛的子集,那么他会喊叫奶牛2,然后是奶牛4,然后是奶牛5。如果 N 头奶牛此时仍未排好顺序他会再次对着这几头奶牛喊叫,如果有必要的话继续重复。
由于FJ不确定哪些奶牛比较专心,他想要使得这个子集最小。此外,他认为 K 是个幸运数字。请帮他求出满足重复喊叫可以使得所有奶牛排好顺序的最小子集之中字典序第 K 小的子集。
我们称 {1,…,N} 的一个子集 S 在字典序下小于子集 T ,当 S 的所有元素组成的序列(按升序排列)在字典序下小于 T 的所有元素组成的序列(按升序排列)。例如, {1,3,6} 在字典序下小于 {1,4,5} 。
题目分析
观察题目,考虑一直对某个集合里的元素一直发号施令会产生什么结果。显然易见,该集合里的所有元素都最终会被排到自己那个最“合适”的位置,而集合外部的元素顺序不会改变。
既然我们要让全部元素从小到大排列,我们要让 选中集合 外部的元素有序。
这样的话,我们必须先求出 “选中集合 外部的元素有序” 的个数。显然,这就是个LIS问题。
设置结构体 Fi? 表示以权值为 i 结尾的 LIS的长度和数量,则可以从权值在 从 1 ?~ i−1?间转移过来,用树状数组维护前缀最大值和数量即可O(n logn)时间内解决。
回到本题,我们可以先用类似的方法,求出以每个点开头的LIS的最长长度和数量。这需要我们倒过来DP,并用树状数组维护后缀和而非前缀和。
在求出这些以后,我们从前往后扫一遍,把每个元素按以它开头的LIS长度,放到n个vector里。(为什么是n个,因为可能序列本身就是有序,长度为n)
然后我们按长度推。对于每个长度,依次遍历这个vector(已经字典序从大到小了),如果当前位置i开头的LIS数量num(i)<k,则k-=num(i),否则我们要选的就是当前位。
注意选了位置i以后,小于i的位置都不能再选,需要把这些位置的LIS数量清零。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const ll Inf=1e18+10; 5 const int MAXN=1e5+10; 6 7 ll k; 8 int n; 9 int a[MAXN]; 10 11 struct Node{ 12 int len;ll num; 13 Node(int len_=0,ll num_=0){len=len_;num=num_;} 14 }BIT[MAXN],f[MAXN]; 15 16 17 inline void Work(Node &x,Node y){ 18 if(x.len>y.len) return; 19 if(x.len<y.len) x=y; 20 else x.num=min(Inf,x.num+y.num); 21 } 22 inline int lowbit(int x){ 23 return x&(-x); 24 } 25 inline void Update(int x,Node y){ 26 for(int i=x;i;i-=lowbit(i)) 27 Work(BIT[i],y); 28 } 29 inline Node Query(int x){ 30 Node res=Node(0,1); 31 for(int i=x;i<=n;i+=lowbit(i)) 32 Work(res,BIT[i]); 33 return res; 34 } 35 vector<int> v[MAXN]; 36 bool used[MAXN]; 37 int main(){ 38 scanf("%d%lld",&n,&k); 39 for(int i=1;i<=n;++i) 40 scanf("%d",&a[i]); 41 for(int i=n;i>=1;--i){ 42 f[i]=Query(a[i]+1); 43 f[i].len++; 44 Update(a[i],f[i]); 45 } 46 for(int i=1;i<=n;++i) 47 v[f[i].len].push_back(i); 48 for(int len=Query(1).len,r=1;len>=1;--len){ 49 for(int i=0;i<(int)v[len].size();++i){ 50 int tmp=v[len][i]; 51 if(f[tmp].num<k) k-=f[tmp].num; 52 else{ 53 used[a[tmp]]=1; 54 while(r<=tmp) f[r++].num=0; 55 break; 56 } 57 } 58 } 59 printf("%d\n",n-Query(1).len); 60 for(int i=1;i<=n;++i) 61 if(!used[i]) 62 printf("%d\n",i); 63 return 0; 64 }
原文地址:https://www.cnblogs.com/LI-dox/p/11217096.html