专栏文章
题解:P10977 Cut the Sequence
P10977题解参与者 5已保存评论 4
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 4 条
- 当前快照
- 1 份
- 快照标识符
- @mir2xscj
- 此快照首次捕获于
- 2025/12/04 14:53 3 个月前
- 此快照最后确认于
- 2025/12/04 14:53 3 个月前
Cut the Sequence
前言
单调队列优化 dp 的好题,思维难度大细节多。因为觉得自己看不懂其他题解,在看完 y 总的讲解后豁然开朗,所以写这篇题解来巩固一下。包括完整的细节分析和思考过程,或许很多大佬都不需要 qwq。叠甲完毕,下面开始正文。
分析
先考虑无解的情况,将单个元素分成段,每段的和最小,如果还是大于 肯定无解。所以当存在 时,输出 。
状态表示
题意和给出的信息都很简单,看 的范围判断大致是 或者 的算法。因为要满足限制且最优化答案,不难想到动态规划来解决。第一维显然是考虑前 个数,通过复杂度分析判断不能存在第二维,实际上也并不需要。所以,设 表示只考虑前 个数划分成若干段,每段的和不超过 的最小代价,代价为每一段的最大值之和。
状态转移
考虑 的集合划分依据,显然是最后一段的长度,设上一个状态为 ,最后一段就是 ,其中 ,而且要满足限制,所以 ,最后一段产生的贡献为 。状态转移方程即为:
优化
上面这个方程显然是 的,不足以通过此题。看式子好像也没啥能转化的,考虑一些性质来优化。
首先注意到 是单调不减的,也就是说 ,简单证明下:
设最后一段的贡献为 ,考虑在末尾加上 。
- 若 加入最后一段,
- 时,有 。
- 时,有 。
- 若 自己新开一段,有 。
因为序列中的数大于等于 ,所以有 ,即 单调不减。
蓝书上的话:
DP 转移优化的指导思想就是及时排除不可能的决策,保持候选集合的高度有效性和秩序性。
所以不妨设 为转移的最优决策,考虑其满足什么样的性质。

设最后一段的贡献为 ,设 为满足 的最小的 。 为 的最大值,下标为 。次大值为 ,下标为 。所以:
- 当 时,, 最小值肯定是在 时,所以最优的 为 。
- 当 时,,最优的 为 。
- 当 时,,最优的 为 。
所以最优决策 为
所以 要成为最优决策,除了要满足 外,还要满足下面条件之一:
- 取 时, 为满足 的最小的 。
- 取 时,满足 。
情况 ,只需要用双指针求 。
情况 ,可以用单调队列维护 ,只需要保证 单调递减。可是有一个问题, 在单调队列中并不一定是单调的,所以要用其他东西维护,要支持加入元素,删除元素,求最小值,可以用平衡树,当然不用自己写,可以用 set,但是值可能有重复,所以使用 multiset。 为队列中的元素,能产生的贡献为
有一个细节,只有当单调队列中的元素大于一时,才能出现第二种情况,思考一下就能理解。
时间复杂度瓶颈主要在于 multiset 的操作,时间复杂度为 。
代码还是很好写的。
code
CPP#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
ll m;
ll a[N],f[N];
int q[N];
multiset<ll> st;
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]>m) //无解的情况
{
cout<<"-1\n";
return ;
}
}
int l=1,r=0;
ll sum=0;
for(int i=1,j=1;i<=n;i++)
{
sum+=a[i];
while(sum>m)
{
sum-=a[j++];
while(l<=r&&q[l]<j)
{
if(l<r) st.erase(f[q[l]]+a[q[l+1]]);
l++;
}
}
while(l<=r&&a[q[r]]<=a[i])
{
if(l<r) st.erase(f[q[r-1]]+a[q[r]]);
r--;
}
q[++r]=i;
if(l<r) st.insert(f[q[r-1]]+a[q[r]]);
f[i]=f[j-1]+a[q[l]];//处理后j=k0+1
if(st.size()) f[i]=min(f[i],*st.begin());
}
cout<<f[n]<<'\n';
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
后记
思维量大,但是代码简单。利用题目性质进行优化 dp。考虑最优决策满足的条件。双指针和单调队列维护。multiset 维护单调队列贡献。
求审核大大通过 qwq。
相关推荐
评论
共 4 条评论,欢迎与作者交流。
正在加载评论...