CF1918D Blocking Elements 题解
如果你做过以下两道题目,那么这道题对你来说会简单得多。
由于题目要求最大值的最小值,考虑二分。
如果我们使用 的费用划分了整个数列,那么我们同样也可以使用比 更多的费用划分这个数列,因为划分方式不用进行改变。所以满足二分条件,可以二分。
考虑二分之后如何让判断是否可以划分整个序列。当我们选择隔断一个数时,会对已经选出的数的总和造成影响。由于这个影响,我们发现很难对这个问题进行贪心判定,所以考虑动态规划。
由于一般的动态规划方式很难对上一个隔断的位置进行维护,所以需要状态中包含位置信息。有一种经典的解决方式,设状态 表示隔断第 个元素,使前 个元素满足条件的最小隔断元素之和,这样每个状态就包含了位置信息。然而,由于最后一个元素并不一定要被隔断,所以统计答案时要将所有在 处隔断后,不再进行隔断,最后一段之和小于当前二分答案值的位置 都统计进去,求最小值。
设 表示数列中的 个元素的值, 为数列 的前缀和, 为二分当前值。根据状态定义,很容易推出以下转移方程:
经过观察,我们发现每次可以转移的状态是根据位置减少的,且每次补充一个新的元素,这是很经典的单调队列优化动态规划的应用模型。使用单调队列维护可转移集合,即可做到 求出所有 的值。
于是,我们就以 的时间复杂度解决了这个问题。
#include <bits/stdc++.h>
using namespace std;
long long t,n,a[300000],s[300000],f[300000];
long long v[300000],p[300000],q=1,h=0;
bool check(long long now)
{
long long ans=1e15;
for(int i=1;i<=n;i++)f[i]=1e15;
q=1,h=0;
v[++h]=0,p[h]=0;
for(int i=1;i<=n;i++)
{
while(s[i-1]-p[q]>now&&q<=h)q++;
f[i]=v[q]+a[i];
while(f[i]<=v[h]&&q<=h)h--;
v[++h]=f[i],p[h]=s[i];
}
for(int i=n;i>=0;i--)
{
if(s[n]-s[i]>now)break;
ans=min(ans,f[i]);
}
return ans<=now;
}
int main()
{
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
long long l=1,r=1e15,ans=0;
while(l<=r)
{
long long mid=(l+r)>>1;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld\n",ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探