好多种解法,自己YY了一种二分答案+trie的做法,不过貌似还是O(nlog^2n)的,看了一下PoPoQQQ大神的题解,发现可以用堆来做,先把每个位置的最小值放入堆里,每次弹出一个元素,假设这个元素是对应位置的第k小,那么把对应位置的第k+1小放入堆中。
一个巧妙的处理,因为会重复,所以取2*k次,只取奇数次的就可以了。
一开始要考虑自己的问题,所以每个位置的第二小值放入堆中。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#define maxn 4000010
using namespace std;
int ch[maxn][2],size[maxn];
int a[maxn];
int tot,n,m,k,mx;
struct yts
{
int w,id,num;
};
struct cmp
{
bool operator() (yts x,yts y)
{
return x.w>y.w;
}
};
priority_queue<yts,vector<yts>,cmp> p;
void insert(int t)
{
int x=0;
for (int i=mx-1;i>=0;i--)
{
int p=(t&(1<<i));
if (p) p=1;
if (!ch[x][p]) ch[x][p]=++tot;
x=ch[x][p];size[x]++;
}
}
int query(int id,int k)
{
int ans=0,x=0;
for (int i=mx-1;i>=0;i--)
{
int p=(a[id]&(1<<i));
if (p) p=1;
if (size[ch[x][p]]>=k) x=ch[x][p];
else ans+=(1<<i),k-=size[ch[x][p]],x=ch[x][p^1];
}
return ans;
}
int LOG(int x)
{
int ans=0;
while (x)
{
ans++;
x>>=1;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) {scanf("%d",&a[i]);mx=max(mx,LOG(a[i]));}
for (int i=1;i<=n;i++) insert(a[i]);
for (int i=1;i<=n;i++)
{
yts t;
t.w=query(i,2);t.id=i;t.num=1;
p.push(t);
}
for (int i=1;i<=2*k;i++)
{
yts t=p.top();p.pop();
if (i&1) printf("%d ",t.w);
if (t.num==n-1) continue;
t.num++;t.w=query(t.id,t.num+1);
p.push(t);
}
printf("\n");
return 0;
}