题目链接
Codeforces Round 1019 (Div. 2) C题
写在前面
补题时这题完全没有思路,看了别人的思路才发现这题挺巧妙的,动态中位数可以用前缀和来求解,之前我一直没做过这类型的题目,遇到这题直接懵了,其实代码挺简单的,主要是要想到如何用前缀和求动态中位数,也是学到新知识了QWQ
题意
把数组分成三段,使得三段的中位数组成的数组的中位数小于等于 k k k。
思路
显然我们要满足题目要求,那最终这三段序列一定满足下列条件之一:
- 有 无 有
- 有 有 无
- 无 有 有
(有:中位数小于等于 k k k,无:中位数大于 k k k)
接下来考虑如何枚举满足的子序列,我们可以用前缀和求动态中位数
如果
a
i
<
=
k
,
它的贡献为
1
,反之它的贡献为
0
a_i<=k,它的贡献为1,反之它的贡献为0
ai<=k,它的贡献为1,反之它的贡献为0 ,然后用一个前缀数组
p
r
e
pre
pre,后缀数组
s
u
f
suf
suf 累加
i
f
(
p
r
e
[
i
]
>
=
(
i
+
1
)
/
2
)
if(pre[i]>=(i+1)/2)
if(pre[i]>=(i+1)/2) 满足,将下标存入
l
l
l 数组,后缀数组同理
接下来考虑上述的3种情况
- l . s i z e ( ) > 2 l.size()>2 l.size()>2 ,则这个序列一定满足:有 有 无
- r . s i z e ( ) > 2 r.size()>2 r.size()>2 ,则这个序列一定满足:无 有 有
- l . f r o n t ( ) + 1 < r . f r o n t ( ) l.front()+1<r.front() l.front()+1<r.front() , l l l数组最小的下标和 r r r数组最大的下标如果不相邻,则这个序列一定满足:有 无 有
- 当 l . s i z e ( ) = 2 l.size()=2 l.size()=2 和 r . s i z e ( ) = 2 r.size()=2 r.size()=2 需要特判,判断中间的序列的中位数是否小于等于 k k k
- 都不满足,输出NO
code
void solve(){
int n,k;
cin >> n >> k;
for(int i=1;i<=n;++i) cin >> a[i];
vector<int> pre(n+5),suf(n+5),l,r;
for(int i=1;i<=n;++i) pre[i]+=pre[i-1]+(a[i]<=k);
for(int i=n;i>=1;--i) suf[i]+=suf[i+1]+(a[i]<=k);
for(int i=1;i<=n;++i){
if(pre[i]>=(i+1)/2) l.push_back(i);
}
for(int i=n;i>=1;--i){
int x=n-i+1;
if(suf[i]>=(x+1)/2) r.push_back(i);
}
if(l.size()>2 || r.size()>2){
cout << "YES" << endl;
return ;
}
if(l.size() && r.size() && l.front()+1<r.front()){
cout << "YES" << endl;
return ;
}
if(l.size()==2){
if(pre[l[1]]-pre[l[0]]>=(l[1]-l[0]+1)/2){
cout << "YES" << endl;
return ;
}
}
if(r.size()==2){
if(suf[r[1]]-suf[r[0]]>=(r[0]-r[1]+1)/2){
cout << "YES" << endl;
return ;
}
}
cout << "NO" << endl;
return ;
}