牛客noip前集训营(第一场)提高T1
链接:https://www.nowcoder.com/acm/contest/172/A
来源:牛客网
题目描述
小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的子区间中,最大的中位数。
备注:
数据范围:
30%: n <= 200
60%: n <= 2000
另外有20%:不超过50个不同的数
100%:1<=Len<=n<=10^5, 1 <= a[i] <= 10^9
题目要求求区间最大中位数。
最暴力的方法就是枚举每一个区间,然后将区间排序,查询中位数。
稍微有话一下上边做法,就维护一个单调序列,查询中位数。
然后看到网上还有一种做法,查询中位数当做查询区间第k大的数,用到了什么叫划分树的东西,枚举区间然后查询,貌似是一有80分。
正解二分答案+前缀和:
既然要二分答案,就需要序列有序,那么另开一个数组,储存序列排序,用于二分。
那么对于每个二分的数,那么我们怎么来判断呢?
对于每个数字x,我们对于每个判断序列中有多少个比x大的数和比x小的数,为了方便判断,预处理一个数组C,原序列中的数大于等于x,C[i]=1,否则C[i]=-1;
我们判断这个数是不是中位数,那么就要让小于它的数和大于等于它的数数量相等。
循环判断,当现枚举的长度大于等于想要的长度时,找出左边最少有多少个比它小的数,往右找到比它大的数。
if(i>=len)ban=min(ban,c[i-len]);
我们每次前缀和,统计+1和-1的数量差。
c[i]+=c[i-1];
满足条件go on!!
if(i>=len&&c[i]-ban>0)return 1;
总的时间复杂度$O(nlongn)$
代码:
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> using namespace std; #define LL long long int read() { int sum=0,fg=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')fg=-1;c=getchar(); } while(c>='0'&&c<='9'){ sum=sum*10+c-'0';c=getchar(); } return sum*fg; } int n,len,a[100006],b[100006]; int c[100006],ban,ans; bool check(int x) { ban=1234567890; for(int i=1;i<=n;i++) if(a[i]>=x)c[i]=1; else c[i]=-1; bool flag=0; for(int i=1;i<=n;i++) { if(a[i]==x)flag=1; if(i>=len)ban=min(ban,c[i-len]); c[i]+=c[i-1]; if(i>=len&&c[i]-ban>0)return 1; } return 0; } int main() { scanf("%d%d",&n,&len); for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+1+n); int l=n/2,r=n; while(l<=r) { int mid=(l+r)/2; if(check(b[mid]))ans=b[mid],l=mid+1; else r=mid-1; } printf("%d",ans); }
除特别注明外,本站所有文章均为Manjusaka丶梦寒原创,转载请注明来自出处