有 n 个房子 每个房子有高度 h 和 美丽度 v
划分几个连续的区域 每个区域的美丽度取最矮房子 求最大的美丽度的和
首先很容易想到一个dp转移式子
dp[i] = max(dp[j] + (v[min(h[k]]));
意思为 前i栋房子中最大的最大的美丽度
很显然这是n^2的复杂度肯定超时
所以我们去想如何维护 后面一段的值 首先我们思考可以用线段树来维护最值
但是运用线段树来维护 如果 h[k]是比h[j]小的话 那么我们就需要 sum[k] - a[j] + a[k]这个很棘手
因为1 ~ n 2~ n … n~n这几个区间取最小的h值 肯定是不递增的
所以如果 当前点 s 为 1 ~ n的最小值 那么 (1~s) ~ n 区间的的最小值 一定为 h(s) 所以我们只需要
将每一段 能确定 左半边的区间 的点存下来就行了 这个时候就需要运用单调栈来维护这个东西
如果当前栈 有 s1 s2 s3
那么 1~ s1区间是已经确定了的 就是a[s1]为最小 只需要加上b[s1]即可
s1 + 1~s2也是确定了的 就是 a[s2]最小 只需要加上 b[s2]即可
s3也是如此
如果来了个 s4 s4 > s1 s4 < s2 s4 < s3
那么s4将顶替s2的位置 所有 s1 ~ s4的点都是用的 a[s4]
所以我们要先把 b[s2],b[s3]分别将对应的区间减掉然后再 加上 b[s4]即可
#include<iostream>
using namespace std;
const int N = 3e5 + 10;
typedef long long ll;
ll tree[N * 4],lazy[N * 4],s[N];
ll a[N],b[N],dp[N];
void pushdown(int s,int t,int p){
tree[p * 2] = tree[p * 2] + lazy[p];
tree[p * 2 + 1] = tree[p * 2 + 1] + lazy[p];
lazy[p * 2] = lazy[p] + lazy[p * 2];
lazy[p * 2 + 1] = lazy[p] + lazy[p * 2 + 1];
lazy[p] = 0;
}
void update(int s,int t,int l,int r,int p,ll x){
if(s >= l && t <= r){
tree[p] += x;
lazy[p] += x;
return;
}
if(lazy[p]) pushdown(s,t,p);
int mid = s + t >> 1;
if(l <= mid) update(s,mid,l,r,p * 2,x);
if(mid < r) update(mid + 1,t,l,r,p * 2 + 1,x);
tree[p] = max(tree[p * 2 + 1],tree[p * 2]);
}
ll query(int s,int t,int l,int r,int p){
if(s >= l && t <= r) return tree[p];
int mid = s + t >> 1;
if(lazy[p]) pushdown(s,t,p);
ll maxn = -0x3f3f3f3f;
if(l <= mid) maxn = max(maxn,query(s,mid,l,r,p * 2));
if(mid < r) maxn = max(maxn,query(mid + 1,t,l,r, p * 2 + 1));
return maxn;
}
void update2(int s,int t,int l,int r,int p,ll x){
if(s >= l && t <= r){
tree[p] = x;
return;
}
if(lazy[p]) pushdown(s,t,p);
int mid = s + t >> 1;
if(l <= mid) update2(s,mid,l,r,p * 2,x);
if(mid < r) update2(mid + 1,t,l,r,p * 2 + 1,x);
tree[p] = max(tree[p * 2 + 1],tree[p * 2]);
}
int main(){
int n,m;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
for(int i = 1; i <= n; i++){
cin >> b[i];
}
int cnt = 1;
a[0] = -1;
dp[1] = 0;
for(int i = 1; i <= n; i++){
while(a[s[cnt]] >= a[i] && cnt){
update(0,n,s[cnt - 1],s[cnt] - 1,1,-b[s[cnt]]);
--cnt;
}
s[++cnt] = i;
update(0,n,s[cnt - 1],s[cnt] - 1,1,b[i]);
dp[i] = query(0,n,0,i - 1,1);
update2(0,n,i,i,1,dp[i]);
}
cout << dp[n] << endl;
return 0;
}
p1295 即将最小值换成了最大值多了一个条件
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
ll dp[N],a[N],b[N],s[N];
ll tree[N * 4],lazy[N * 4];
void pushdown(int s,int t,int p){
tree[p * 2] = tree[p * 2] + lazy[p];
tree[p * 2 + 1] = tree[p * 2 + 1] + lazy[p];
lazy[p * 2] = lazy[p] + lazy[p * 2];
lazy[p * 2 + 1] = lazy[p] + lazy[p * 2 + 1];
lazy[p] = 0;
}
void update(int s,int t,int l,int r,int p,ll x){
if(s >= l && t <= r){
tree[p] += x;
lazy[p] += x;
return;
}
if(lazy[p]) pushdown(s,t,p);
int mid = (s + t) >> 1;
if(l <= mid) update(s,mid,l,r,p * 2,x);
if(mid < r) update(mid + 1,t,l,r,p * 2 + 1,x);
tree[p] = min(tree[p * 2 + 1],tree[p * 2]);
}
ll query(int s,int t,int l,int r,int p){
if(s >= l && t <= r) return tree[p];
int mid = (s + t) >> 1;
if(lazy[p]) pushdown(s,t,p);
ll maxn = 0x3f3f3f3f;
if(l <= mid) maxn = min(maxn,query(s,mid,l,r,p * 2));
if(mid < r) maxn = min(maxn,query(mid + 1,t,l,r, p * 2 + 1));
return maxn;
}
void update2(int s,int t,int l,int r,int p,ll x){
if(s >= l && t <= r){
tree[p] = x;
return;
}
if(lazy[p]) pushdown(s,t,p);
int mid = (s + t) >> 1;
if(l <= mid) update2(s,mid,l,r,p * 2,x);
if(mid < r) update2(mid + 1,t,l,r,p * 2 + 1,x);
tree[p] = min(tree[p * 2 + 1],tree[p * 2]);
}
int main(){
int n,m;
cin >> n >> m;
for(int i = 1; i <= n; i++){
scanf("%lld",&a[i]);
}
int cnt = 1;
int last = 0;
a[0] = 0x3f3f3f3f;
for(int i = 1; i <= n; i++){
b[i] = b[i - 1] + a[i];
while(b[i] - b[last] > m) last++;
while(a[s[cnt]] <= a[i] && cnt){
update(0,n,s[cnt - 1],s[cnt] - 1,1,-a[s[cnt]]);
--cnt;
}
s[++cnt] = i;
update(0,n,s[cnt - 1],s[cnt] - 1,1,a[i]);
dp[i] = query(0,n,last,i - 1,1);
update2(0,n,i,i,1,dp[i]);
}
cout << dp[n] << endl;
return 0;
}