题目描述
小N得到了一个非常神奇的序列A。这个序列长度为N,下标从1开始。A的一个子区间对应一个序列,可以由数对[l,r]表示,代表A[l], A[l + 1], ..., A[r]这段数。对于一个序列B[1], B[2], ..., B[k],定义B的中位数如下:
1. 先对B排序。得到新的序列C。
2. 假如k是奇数,那么中位数为。假如k为偶数,中位数为。
对于A的所有的子区间,小N可以知道它们对应的中位数。现在小N想知道,所有长度>=Len的子区间中,中位数最大可以是多少。
输入描述:
第一行输入两个数N,Len。
第二行输入序列A,第i个数代表A[i]。
输出描述:
一行一个整数,代表所有长度>=Len的子区间中,最大的中位数。
示例1
输入
11 3
4864 8684 9511 8557 1122 1234 953 9819 101 1137 1759
输出
8684
备注:
数据范围:
30%: n <= 200
60%: n <= 2000
另外有20%:不超过50个不同的数
100%:1<=Len<=n<=10^5, 1 <= a[i] <= 10^9
Solution:
本题ZYYS,考场上打了个主席树水了60分,实际上能水80分的,但数组开小了GG,然后正解是二分。
对原数列排序,二分某一数$x$作为答案,那么只要判断$x$是否能作为合法的中位数。
判断的过程理解为原数列是否存在大于等于$x$的中位数,对于大于等于$x$的数赋值为1,小于$x$的数赋值为$-1$,然后求前缀和,那么只需判断是否存在一段合法的区间$r-l+1>=len$使得$s[r]-s[l-1]\geq 0$即可,贪心的想到合法的$s[l-1]$越小越好,所以记录下合法的前缀和最小值,然后直接$O(n)$扫一遍判断就好了。
时间复杂度$O(n\log n)$。
代码:
/*Code by 520 -- 9.9*/
#include<bits/stdc++.h>
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=;
int n,m,a[N],b[N],c[N]; int gi(){
int a=;char x=getchar();
while(x<''||x>'')x=getchar();
while(x>=''&&x<='')a=(a<<)+(a<<)+(x^),x=getchar();
return a;
} il bool check(int tp){
int minn=0x7fffffff;
For(i,,n) c[i]=c[i-]+(a[i]>=tp?:-);
For(i,m,n) {
minn=min(minn,c[i-m]);
if(c[i]>minn)return ;
}
return ;
} int main(){
n=gi(),m=gi();
For(i,,n) a[i]=b[i]=gi();
sort(b+,b+n+);
int l=,r=n;
while(l<=r){
RE int mid=l+r>>;
if(check(b[mid])) l=mid+;
else r=mid-;
}
cout<<b[r];
return ;
}