题目来源
暴力判断每个位置填什么,时间复杂度O(n^2)。
数据范围显然需要一个O(nlogn)的做法。
中位数只需要知道两个数之间的大小关系——并不需要知道数字具体的大小。
题解告诉我们可以用二分查找。
我们发现 查找一个数,比他大的数标1,比他小的数标0.
一个格子下面三个格子中如果有2个及以上相同数字,那么那个数的中位数肯定就是在这两个中。
所以我们发现,连续0 1区间的正上方会被覆盖,全是相同数字。
而01交错的区间,最上方覆盖的区域也是交错的。我们只考虑最上方覆盖的区域,把这段01对半区间对半劈开,左部分等同于左边的贡献,右部分等同于右边的贡献。
注意特判最下层全是01交错的情况。注意左右两端点连续区间和01区间合并的时候的特判。不要把a数组覆盖。。
这样每判断一次是O(n),整体复杂度(nlogn)。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n,N,a[MAXN<<1],b[MAXN<<1];
struct data{
int l,r,val;
}d[MAXN];
int re(int limit){
memset(b,-1,sizeof(b));
int num=0;
for(int i=1;i<=N;i++){
b[i]=a[i]>=limit?1:0;
}
for(int i=1;i<N;i++){
if(b[i]==b[i+1]){
d[++num].l=i;
d[num].val=b[i];
while(i<=N&&b[i]==b[i+1]){
++i;
}
d[num].r=i;
}
}
//cout<<num;
if(!num)return b[1];
d[0].r=0;
for(int i=1;i<=num;i++){
int l1=d[i-1].r+1,r1=d[i].l-1;//交替区域
if(l1>r1)continue;
int mid=(l1+r1)>>1;
d[i-1].r=mid;d[i].l=mid+1;
}
//注意1和num的,两头的范围。
int l1=d[num].r+1,r1=N;
if(l1<=r1){
int mid=(l1+r1)>>1;
d[num].r=mid;
}
for(int i=1;i<=num;i++){if(d[i].l<=n&&d[i].r>=n)return d[i].val;}
}
int main(){
scanf("%d",&n);
N=2*n-1;
for(int i=1;i<=N;i++){
scanf("%d",&a[i]);
}
int l=1,r=N,ans;
while(l<=r){
int mid=(l+r)>>1;
if(re(mid))ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}