CodeForces 1482E Skyline Photo dp +线段树+ 单调 P1295 [TJOI2011]书架 类似

有 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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值