Hetao P1156 最大战力 题解 [ 绿 ][ 二分 ][ 最大子段和 ]
题解
形式化题意
给定两个数组
概述
本题思维难度较大,需要将中位数的浮动转化为
但是直接这样做,可能会得到并不是最大的的中位数,因为如果我们选择对一个静态的区间计算贡献,则可能在有多个最大贡献的子段和的情况下( 因为贡献的值只可能是
因此,我们可以倒着来,在保证中位数合法的情况下,使中位数最大,而不是用贡献来直接确定最大中位数。这个过程需要二分中位数来实现。
总的时间复杂度为
分析
二分
首先,要明确有奇数个元素的序列的中位数的求法。
1.把原数组排序,第
2.采用快速选择算法,即快速排序的简单应用,时间
3.二分中位数,把
可以注意到,对于 二分中位数 的做法,我们可以在二分时做一些手脚,对于当前二分的中位数
而判断一个中位数是否合法,就可以用前文中的标记总和的大小来判断。
这是二分答案题的基本套路。
最大子段和
有了二分,那么接下来就该实现选择区间,使中位数最大的模块了。
下文中,我们记
首先可以发现,标记的功能可以看做是一个一个贡献,比当前二分的数
而修改的区间则也需要这样标记,因为它可能被替换进原数组中。
有了这些标记,我们就可以计算出替换一个数的贡献
因为改了一个数,如果原本它
如果原本它
但如果修改前和修改后相对中位数
求完
最大子段和的做法是对每一个
注意,最大子段和在所有元素为负数时,会选择空区间,修改贡献总和为
常见问题
Q1 : 修改一个数后,如果它的中位数变化了,那么之前的标记是否会失效?
A1 : 不会失效。
对于修改一个区间
因此,修改一个区间
Q2 : 能否在一个元素
A2: 不能。
hack: 1 , 2 , 3 , 3 , 3 , 3 , 3 , 4 , 5
___________________↑ ___________________
这时候有多个中位数,如果标记成
坑点
本题二分答案时
十年OI一场空,不开long long见祖宗
代码
#include<bits/stdc++.h>
using namespace std;
int n,a[300005],b[300005],c[300005];
int check(int mid)
{
int res=0;
for(int i=1;i<=n;i++)
{
if(a[i]>=mid)res++;
else res--;
//这里省略了标记 a[i] b[i] 的步骤,直接赋 c[i] 的值
if(a[i]>=mid && b[i]>=mid)c[i]=0;
else if(a[i]>=mid && b[i]<mid)c[i]=-2;
else if(a[i]<mid && b[i]>=mid)c[i]=2;
else c[i]=0;
}
int minres=0,sumn=0,maxres=0;
for(int i=1;i<=n;i++)
{
sumn+=c[i];
maxres=max(sumn-minres,maxres);
minres=min(minres,sumn);
}
return res+maxres;
}
int main()
{
freopen("yone.in","r",stdin);
freopen("yone.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i]>>b[i];
}
long long l=0,r=2e9+10,mid;
while(l<r)
{
mid=((l+r+1)>>1);//l+r加到最大时会爆int
if(check(mid)>=0)l=mid;
else r=mid-1;
}
cout<<l;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战