传送门
题解:
这种不可枚举的数量里面求第 K K K 大显然是二分。
问题在于这道题似乎并不好直接二分,没法对 O ( n 2 ) O(n^2) O(n2) 个串确定顺序。
首先注意到这里的大小关系给的非常的迷,这就导致有一点很妙,对于一个集合,新加一个数,得到的集合一定比原集合小。
于是确定左端点的时候,集合大小关于右端点单调。
现在还有一个无法二分的问题,这个完全不用在意,在可能集合中随机一个集合即可,容易证明期望随机次数 O ( log n ) O(\log n) O(logn)。
那么只剩最后一个问题,计算选中集合的排名。
考虑计算有多少个比它大的,也就是它是第几大的。
由于集合在单独增加一个元素或减少一个元素时候的单调性有保证,做法看下面的代码。
比较两个集合大小可以值域作差,具体实现可以考虑线段树。
还有要注意就是二分不一定要二分到只剩下一个,剩下的全部相等的时候也可以直接退出。
不过懒的话就像我一样直接次数爆炸的时候退出即可。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}
inline ll gl(){return get_integer<ll>();}
}using namespace IO;
using std::cerr;
using std::cout;
cs int N=1e5+7;
std::mt19937 rn(time(0));
std::uniform_int_distribution<int> rnd(0,(int)1e9);
ll K;
int n,a[N];
namespace SGT{
cs int M=1<<17;
int ct[M<<1|1];
int les[M<<1|1];
void clr(){
memset(ct,0,sizeof ct);
memset(les,0,sizeof les);
}
bool push_up(int p){
int c=ct[p],l=les[p];
int lc=p<<1,rc=lc|1;ct[p]=ct[lc]||ct[rc];
les[p]=les[lc]||(!ct[lc]&&les[rc]);
return c!=ct[p]||l!=les[p];
}
void add(int p,int v){
ct[p+=M]+=v,les[p]=ct[p]<0;
while((p>>=1)&&push_up(p));
}
}
int lim[N];
ll solve(int ql,int qr){
ll ct=0;SGT::clr();
for(int re i=ql;i<=qr;++i)
SGT::add(a[i],1);
for(int re i=1;i<=n;++i){
lim[i]=std::max(lim[i-1],i-1);
while(lim[i]<=n&&!SGT::les[1])
SGT::add(a[++lim[i]],-1);
if(i<=lim[i])SGT::add(a[i],1);
ct+=lim[i]-i;
}return ct;
}
int st[N],tp,L[N],R[N];
void Main(){
n=gi(),K=gl();
for(int re i=1;i<=n;++i)
a[i]=gi();K=n*(n+1ll)/2+1-K;
for(int re i=1;i<=n;++i)
L[i]=i,R[i]=n,st[++tp]=i;
for(int re T=1;T<=80;++T){
int p=st[rnd(rn)%tp+1];
int pos=rnd(rn)%(R[p]-L[p]+1)+L[p];
if(solve(p,pos)>=K)
for(int re i=1;i<=tp;++i)
R[st[i]]=std::min(R[st[i]],lim[st[i]]-1);
else
for(int re i=1;i<=tp;++i)
L[st[i]]=std::max(L[st[i]],lim[st[i]]);
int ct=0;
for(int re i=1;i<=tp;++i)
if(L[st[i]]<=R[st[i]])
st[++ct]=st[i];
tp=ct;
if(tp==1&&L[st[1]]==R[st[1]])break;
}int l=st[1],r=R[st[1]];
std::sort(a+l,a+r+1);
for(int re i=l;i<=r;++i)
cout<<a[i]<<" ";
}
inline void file(){
#ifdef zxyoi
freopen("multisets.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
freopen("multisets.in","r",stdin);
freopen("multisets.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}