版权声明:是自己手打的没错 https://blog.csdn.net/Mr_Treeeee/article/details/82698797
https://arc101.contest.atcoder.jp/tasks/arc101_b
题意:
给你一个序列。对于每个区间[l,r]都会有一个中位数,为x[len/2+1]。
所以共有(1+n)*n/2个中位数,这些中位数再取中位数。问你这个中位数是多少。
POINT:
二分答案,假设x是那么多中位数序列b的中位数,即答案。
那么b中一定有至少个元素>=x。 然后x为最大的且满足条件。
所以问题就变成了,计算有多少个区间,他的中位数>=x。
把原序列改变一下,如果元素>=x,变为1,反之变为-1。
然后求区间和,区间和>=0的话,他的中位数肯定>=x(这个是重点)。
理解了这个之后,问题就变成了如何快速求有多少个区间和>=0。
求一个前缀和sum[]。 那么sum[r]-sum[l-1]>=0,就表示区间和>=0。
即sum[r]>=sum[l-1]。 可以用逆序对的方法求得。
不过sum可能为负数,所以还是用归并求吧。用树状数组比较麻烦了。
只是做了一点微小的翻译工作。
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
using namespace std;
#define LL long long
const int N = 1e5+5;
int a[N],sum[N],n;
int temp[N];
LL mergesort(int l,int r)
{
if(l>=r) return 0;
int mid=(l+r)>>1;
LL ans=mergesort(l,mid)+mergesort(mid+1, r);
int ll=l,rr=mid+1;
int cnt=0;
while(ll<=mid&&rr<=r){
if(sum[rr]>=sum[ll]){
temp[++cnt]=sum[ll++];
}else{
ans+=ll-l;
temp[++cnt]=sum[rr++];
}
}
while(ll<=mid){
temp[++cnt]=sum[ll++];;
}
while(rr<=r){
ans+=mid-l+1;
temp[++cnt]=sum[rr++];
}
for(int i=1;i<=cnt;i++){
sum[l+i-1]=temp[i];
}
return ans;
}
LL check(int x)
{
LL ans=0;
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+(a[i]>=x?1:-1);
if(sum[i]>=0)
ans++;
}
return ans+mergesort(1,n);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
int l=1,r=1e9;
LL aim=(1LL+n)*n/2;
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)>=(aim+1)/2){
l=mid;
}else{
r=mid-1;
}
}
cout<<l<<endl;
return 0;
}