目录:
题目:
分析:
对于20%的数据:
直接暴力
对于40%的数据:
显然的,对于一个序列的终极数,肯定就是这个序列的中位数,那么可以用快排,做到
对于100%的数据:
找中位数,可以用堆:
我们维护两个堆,一个大根堆,一个小根堆,当前每次加入一个数,必须保证
小根堆的个数大于等于大根堆的个数,亦即——第一次加入数的时候肯定是放
到最小堆。
其次,我们还需保证小根堆里的每个值必须大于等于大根堆当中的最大值。
并且,我们当前大根堆的个数如果已经小于小根堆的话,则要把下一个数加到
大根堆里,反之亦然。
而当我们加入一个数时,会有两种特殊情况:
设加入的数为
,如果欲加入到大根堆里时,
大于小根堆的堆顶,则进行
操作。
反之——如果欲加入到小根堆里时,
小于大根堆的堆顶,则进行
操作。
把
放到小根堆中,并且把小根堆的堆顶放到大根堆当中,并维护两堆
性质。
把
放到大根堆中,并且把大根堆的堆顶放到小根堆当中,并维护两堆
性质。
最后我们再对所有的终极数
进行一次排序,找出最小的终极数
即可,可证至多有两个终极数。
时间复杂度
代码:
#pragma GCC optimize(3)
//O3大法好,用了直接少200ms
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int x[500005],y[500005],zj[1000001],n,xs=0,ys=0;
//x为小根堆,y为大根堆
void upx(int w)
{
while(w>1&&x[w/2]>=x[w]) swap(x[w],x[w/2]),w/=2;
return;
}
void upy(int w)
{
while(w>1&&y[w/2]<=y[w]) swap(y[w],y[w/2]),w/=2;
return;
}
void downx(int w)
{
int a;
while(w*2<=xs&&x[w]>x[w*2]||w*2+1<=xs&&x[w]>x[w*2+1])
{
a=w*2;
if(a+1<=xs&&x[a]>x[a+1]) a++;
swap(x[w],x[a]);
w=a;
}
return;
}
void downy(int w)
{
int a;
while(w*2<=ys&&y[w]<y[w*2]||w*2+1<=ys&&y[w]<y[w*2+1])
{
a=w*2;
if(a+1<=ys&&y[a]<y[a+1]) a++;
swap(y[w],y[a]);
w=a;
}
return;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
int a;
n=read();
for(int i=1;i<=n;i++)
{
a=read();
if(xs<=ys)
{
if(a<y[1])
{
x[++xs]=y[1];
upx(xs);
downx(1);
y[1]=a;
downy(1);
}
else
{
x[++xs]=a;
upx(xs);
downx(1);
}
}
else
{
if(a>x[1])
{
y[++ys]=x[1];
upy(ys);
downy(1);
x[1]=a;
downx(1);
}
else
{
y[++ys]=a;
upy(ys);
downy(1);
}
}
zj[i]=x[1];
}
sort(zj+1,zj+n+1);
printf("%d",zj[n/2]);
fclose(stdin);
fclose(stdout);
return 0;
}