线段树的启发——P1295 [TJOI2011] 书架

原题 P1295 [TJOI2011] 书架

启发

线段树能维护的操作不止简单运算,只要是两项能够合并,并且可以整体修改的都可以考虑线段树

这道题维护了节点 \(k\) 的 \(f_{k-1}\) 和 \(f_{k-1}+\max_{j=k}^ih_j\)(\(f_{i}\) 是 \(i\) 时的答案)

已知 \(h\) 更新 \(f_{k-1}+\max_{j=k}^ih_j\) 时直接用 \(f_{k-1}\) + \(h\),然后取区间最值

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mid ((l+r)>>1)
#define ls p*2
#define rs p*2+1
#define lson ls,l,mid
#define rson rs,mid+1,r
const ll inf=1e12;const int N=1e5+5;
int pre[N],sta[N],top,n;
ll h[N],f[N],m,sum[N];
struct b{ll f,ans,tag;}t[N*4];
void pushup(int p){
	t[p].f=min(t[ls].f,t[rs].f),t[p].ans=min(t[ls].ans,t[rs].ans);
}
void pushdown(int p){
	if(t[p].tag==inf) return;
	t[ls].ans=t[ls].f+t[p].tag,t[rs].ans=t[rs].f+t[p].tag;
	t[ls].tag=t[rs].tag=t[p].tag;
	t[p].tag=inf;
}
void build(int p,int l,int r){
	t[p]={inf,inf,inf};
	if(l==r) return;
	build(lson),build(rson);
}
void modify(int p,int l,int r,int x){
	if(l==r){
		t[p].f=f[x-1],t[p].ans=inf;
		return;
	}
	pushdown(p);
	if(x<=mid) modify(lson,x);
	else modify(rson,x);
	pushup(p);
}
void update(int p,int l,int r,int L,int R,int v){
	if(L<=l&&r<=R){
		t[p].tag=v,t[p].ans=t[p].f+v;
		return;
	}
	pushdown(p);
	if(L<=mid) update(lson,L,R,v);
	if(R>mid) update(rson,L,R,v);
	pushup(p);
}
ll query(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R) return t[p].ans;
	pushdown(p);
	ll tmp=inf;
	if(L<=mid) tmp=min(tmp,query(lson,L,R));
	if(R>mid) tmp=min(tmp,query(rson,L,R));
	return tmp;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>h[i];
		while(top&&h[sta[top]]<h[i]) top--;
		pre[i]=sta[top],sta[++top]=i;
		sum[i]=sum[i-1]+h[i];
	}
	build(1,1,n);
	for(int i=1;i<=n;i++){
		modify(1,1,n,i);
		if(pre[i]<i) update(1,1,n,pre[i]+1,i,h[i]);
		int l=lower_bound(sum,sum+i+1,sum[i]-m)-sum;
		f[i]=query(1,1,n,l+1,i);
	}
	cout<<f[n];
	return 0;
}

​

posted @ 2026-10-30 14:53  Rainesxiao  阅读(4)  评论(0)    收藏  举报