题解:P10977 Cut the Sequence
solution
记 \(dp_i\) 表示前 \(i\) 个已经分好段,有状态转移:\(dp_i = dp_{j - 1} + \displaystyle\max_{k=j}^{i}\{a_k\}\),满足 \(\displaystyle\sum^i_{k=j}{a_k} \leq m\) 前提下,复杂度 \(O(n^2)\),无法接受。
考虑优化,从单调性先入手,对于一个不变的 \(i\),当 \(j\) 单增时,\(\displaystyle\max_{k=j}^{i}\{a_k\}\) 单调不增,\(dp_{j-1}\) 单调不减,在我们枚举到 \(i\) 时,\(dp_{j-1}\) 并不会对转移产生影响。
考虑 \(a_i\) 对区间的影响,每次找到 \(a_{pos} > a_i\),其中 \(pos \in [1,i)\),显然可以发现对于 \(\displaystyle\max_{k=j}^{i}\{a_k\}(j > pos) \rightarrow a_i\),考虑线段树维护这个操作,同时维护 \(dp_i\) 的转移。
需要进行的操作如下:
- 当前 \(j\) 的 \(dp_{j-1}\),单点修改。
- 上文提到的操作,区间修改。
- \(dp_i\) 的转移,同时区间查询最小值。
每次二分找到满足条件的区间右端点的后继 \(ptr\),查询区间即为 \([ptr + 1,i]\),最终答案 \(dp_{n}\),完结撒花!
code
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
template <typename T>
inline void read(T &x){
T res = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)){if(ch == '-') f = -1;ch = getchar();}
while(isdigit(ch)){res = (res << 1) + (res << 3) + (ch ^ 48);ch = getchar();}
x = res * f;
}
const int maxn = 1e5 + 10,inf = 1e18;
int n,m,a[maxn],st[maxn],top,apr[maxn],dp[maxn],sum[maxn];
struct node{int l,r,Answer,lazy,dp;}tree[maxn << 4];
void pushup(int rt){
tree[rt].Answer = min(tree[rt << 1].Answer,tree[rt << 1 | 1].Answer);
tree[rt].dp = min(tree[rt << 1].dp,tree[rt << 1 | 1].dp);
}
void pushdown(int rt){
if(tree[rt].lazy != inf){
tree[rt << 1].Answer = tree[rt << 1].dp + tree[rt].lazy;
// cout << tree[rt << 1].Answer << " " << tree[rt << 1].dp << " " << tree[rt].lazy << endl;
tree[rt << 1 | 1].Answer = tree[rt << 1 | 1].dp + tree[rt].lazy;
tree[rt << 1].lazy = tree[rt << 1 | 1].lazy = tree[rt].lazy;
tree[rt].lazy = inf;
}
}
void Build(int l,int r,int rt){
tree[rt].l = l,tree[rt].r = r;
if(l == r){
tree[rt].Answer = tree[rt].dp = tree[rt].lazy = inf;
return;
}
int mid = l + r >> 1;
Build(l,mid,rt << 1);
Build(mid + 1,r,rt << 1 | 1);
pushup(rt);
}
void Update(int ql,int qr,int rt,int tag){
int l = tree[rt].l,r = tree[rt].r;
if(ql <= l && qr >= r)
{
tree[rt].Answer = tree[rt].dp + tag;
tree[rt].lazy = tag;
return;
}
pushdown(rt);
int mid = l + r >> 1;
if(ql <= mid)
Update(ql,qr,rt << 1,tag);
if(qr > mid)
Update(ql,qr,rt << 1 | 1,tag);
pushup(rt);
}
void Modify(int pos,int rt){
int l = tree[rt].l,r = tree[rt].r;
if(l == r){
tree[rt].Answer = inf;
tree[rt].dp = dp[pos - 1];
return;
}
int mid = l + r >> 1;
pushdown(rt);
if(pos <= mid)
Modify(pos,rt << 1);
else
Modify(pos,rt << 1 | 1);
pushup(rt);
}
int query(int ql,int qr,int rt){
int l = tree[rt].l,r = tree[rt].r;
if(ql <= l && qr >= r)
return tree[rt].Answer;
pushdown(rt);
int mid = l + r >> 1,val = inf;
if(ql <= mid)
val = min(val,query(ql,qr,rt << 1));
if(qr > mid)
val = min(val,query(ql,qr,rt << 1 | 1));
return val;
}
inline void init(){
read(n),read(m);
for(int i = 1;i <= n;i ++) {
read(a[i]),sum[i] = sum[i - 1] + a[i];
if(a[i] > m){
printf("-1\n");
exit(0);
}
}
st[++ top] = 1;
for(int i = 2;i <= n;i ++){
while(top && a[st[top]] < a[i]) top --;
if(top) apr[i] = st[top];
st[++ top] = i;
}
// 预处理 a[pos] > a[i]
Build(1,n,1);
}
inline void Solve(){
int Answer = inf;
bool flagAns = 1;
for(int i = 1;i <= n;i ++){
Modify(i,1);
if(apr[i] < i) Update(apr[i] + 1,i,1,a[i]);
int ptr = lower_bound(sum,sum + i + 1,sum[i] - m) - sum;// 二分找到满足条件的下标
if(ptr < i) dp[i] = query(ptr + 1,i,1);
else{// 没有满足的无解
flagAns = 0;
break;
}
}
if(!flagAns)
printf("-1\n");
else
printf("%lld\n",dp[n]);
}
signed main(){
init();
Solve();
return 0;
}