AtCoder Regular Contest 101 - D Median of Medians (二分+偏序)

版权声明:是自己手打的没错 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中一定有至少\left \lceil len/2 \right \rceil个元素>=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;
}

猜你喜欢

转载自blog.csdn.net/Mr_Treeeee/article/details/82698797