题解:P10579 [蓝桥杯 2024 国 A] 最长子段
DeepSleep_Zzz · · 题解
begin
P10579 [蓝桥杯 2024 国 A] 最长子段
Part 0 前置芝士
前缀和,二分。
Part 1 题目分析
题意已经清晰了,无需多言。
这题一个最明显也是最好想的思路就是暴力,但是显然时间复杂的是 但是你拿到 60 pts 后可以去借鉴别人代码了。
我们思考一下比
经过简单的思考深思熟虑过后,我们发现,这题用
再思考一下,由于
很显然,这题跟排序连一分钱关系都没有,所以我们最终锁定二分。
Part 2 解题思路
二分最重要的就是单调性,没有一个单调性的数组这一切就全都是空谈。所以我们现在的问题就是如何构建一个与题目有关的且具有单调性的数组呢?
我们将目光放在题目所给的那一大段条件上(以下我们记
我们考虑用一个数组
为什么要取
当
- 如果
f_i=f_{i-1} ,它对答案的贡献就是:R-(i-1)+1=R-i+2 - 如果
f_i=sum_{i-1}-a \times c \times i ,它对答案的贡献是:R-i+1 显然前者更优,足足比后者大了
1 呢!
这也同时保证了
Part 3 Tips
到了这里,这道题你就已经完成 90% 了。但是正所谓行百里者半九十,所以最后的写代码环节也是至关重要的。如果你搞不定细节,那一样完蛋。
大前提:
-
我用的二分方法是
while (l+1<r),如果你的方法和我不同的话可以参考一下思路。 -
以下我们设
sum_R-a \cdot b \cdot R=x 。
Part 3.1
首先需要注意的就是二分时
我们来举个栗子:若
显然
Part 3.2
然后就是每个二分的易错点:内部判断条件是 if (f[mid]<=x) r=mid 还是 if (f[mid]<x) r=mid?
要找到这个问题的答案,我们首先要知道我们的目标是找到
所以后者是正确的。
Part 4 Code
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
ll n,a,b,c,sum[300010],now,f[300010],x,l,r,mid,pos,ans;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); // 输入输出加速
cin>>n>>a>>b>>c;
for (ll i=1;i<=n;i++)
{
cin>>now; // 省点空间
sum[i]=sum[i-1]+now; // 前缀和
}
f[0]=1e18; // 是极大值就行,防止第一次 min 出祸
for (ll i=1;i<=n;i++)
{
f[i]=min(f[i-1],sum[i-1]-a*c*i); // 求 f 数组
x=sum[i]-a*b*i; // x
l=0,r=i+1;
while (l+1<r)
{
mid=(l+r)>>1;
if (f[mid]<x)
{
r=mid;
}
else
{
l=mid;
}
}
if (i>=r)
{
ans=max(ans,i-r+1); // 维护最大答案
}
}
cout<<ans;
return 0; // 完结撒花~
}
end