luogu P1848 [USACO12OPEN] Bookshelf G 题解
link
定义 \(dp_i\) 为考虑到第 \(i\) 位的答案,那么转移就为 \(dp_i=\max\limits_jdp_j+\max\limits_{k=j+1}^{i}h_i\)。
当你要转移到 \(dp_i\) 时,前面的 \(dp\) 值相当于加了一个 \(h\) 的后缀最大值。我们考虑把 \(dp\) 拍到线段树上,维护三个变量 \(mn1,mn2,mn3\),分别表示 \(dp\) 的最小值,\(dp\) 要加的后缀最大值的最小值以及 \(dp\) 加上后缀最大值的最小值。当添加第 \(i\) 个元素的时候,可以用单调栈维护出有哪些 \(dp\) 值会受到影响,然后进行区间修改。因为你修改时一个区间里面新的后缀最大值都是相同的,所以可以直接修改 \(dp\) 加后缀最大值的值。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lc(i) ((i)<<1)
#define rc(i) ((i)<<1|1)
const int N=1e5+5;
const ll inf=1e18;
int n,L,h[N],w[N];ll dp[N],ans=inf,mn1[N<<2],mn2[N<<2],mn3[N<<2],cov[N<<2];
stack<int> st;
void pushup(int i){
mn1[i]=min(mn1[lc(i)],mn1[rc(i)]);
mn2[i]=min(mn2[lc(i)],mn2[rc(i)]);
mn3[i]=min(mn3[lc(i)],mn3[rc(i)]);
}
void apply_cov(int i,ll v){
mn2[i]=v;
mn3[i]=mn1[i]+v;
cov[i]=v;
}
void pushdown(int i){
if(cov[i]){
apply_cov(lc(i),cov[i]);
apply_cov(rc(i),cov[i]);
cov[i]=0;
}
}
void update(int i,int tl,int tr,int l,int r,ll v){
if(l<=tl&&tr<=r){
apply_cov(i,v);
return;
}
pushdown(i);
int tm=(tl+tr)>>1;
if(l<=tm)update(lc(i),tl,tm,l,r,v);
if(r>tm)update(rc(i),tm+1,tr,l,r,v);
pushup(i);
}
void upd(int i,int tl,int tr,int p,ll v){
if(tl==tr){
mn1[i]=v;
mn3[i]=mn1[i]+mn2[i];
return;
}
pushdown(i);
int tm=(tl+tr)>>1;
if(p<=tm)upd(lc(i),tl,tm,p,v);
else upd(rc(i),tm+1,tr,p,v);
pushup(i);
}
ll query(int i,int tl,int tr,int l,int r){
if(l<=tl&&tr<=r)return mn3[i];
pushdown(i);int tm=(tl+tr)>>1;ll res=inf;
if(l<=tm)res=query(lc(i),tl,tm,l,r);
if(r>tm)res=min(res,query(rc(i),tm+1,tr,l,r));
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>L;
for(int i=1;i<=n;i++){
cin>>h[i]>>w[i];
w[i]+=w[i-1];
}
for(int i=1,dn=1;i<=n;i++){
while(!st.empty()&&h[st.top()]<=h[i])st.pop();
int l=(st.empty()?0:st.top());
st.push(i);
while(w[i]-w[dn-1]>L)dn++;
update(1,0,n,l+1,i,h[i]);
dp[i]=query(1,0,n,dn,i);
upd(1,0,n,i+1,dp[i]);
}
cout<<dp[n];
return 0;
}

浙公网安备 33010602011771号