1. 程式人生 > 實用技巧 >[線段樹][二分][DP]luogu P1295 [TJOI2011]書架

[線段樹][二分][DP]luogu P1295 [TJOI2011]書架

題面

https://www.luogu.com.cn/problem/P1295

分析

很容易想到設 $f_i$ 表示選到第 $i$ 個的最小最大值之和,則有

$f_i=min(f_j+max(hj~hi) (j<i)$

發現這裡有一段連續的東西,然後考慮線段樹。

考慮插入一個 $h_i$ 的影響,就會使從 $i$ 向前找到的第一個 $j(h[j-1]>=h[i])$ 的 $[i,j]$ 區間 $h$ 值全部設為 $h_i$

那麼開三棵線段樹,一個維護 $f$ ,一個維護 $h$ ,一個維護 $max(h_j~h_i)$

查詢 $h$ 最值時二分即可

程式碼

#include <iostream>
#include 
<cstdio> #define lson (x<<1) #define rson ((x<<1)+1) using namespace std; typedef long long L; const L Inf=1ll<<62; const int N=1e5+10; int n,m,h[N],rev[N]; L t[4*N],lz[4*N],f[4*N],g[4*N]; void Pushdown(int x) {if (!lz[x]) return;g[lson]=f[lson]+lz[x];g[rson]=f[rson]+lz[x];lz[lson]=lz[rson]=t[lson]=t[rson]=lz[x];lz[x]=0
;} void Change(int x,int l,int r,int ll,int rr,int val) { if (r<l) return; if (ll<=l&&r<=rr) {g[x]=f[x]+val;t[x]=lz[x]=val;return;} int mid=l+r>>1; Pushdown(x); if (ll<=mid) Change(lson,l,mid,ll,rr,val); if (mid<rr) Change(rson,mid+1,r,ll,rr,val); t[x]
=max(t[lson],t[rson]);f[x]=min(f[lson],f[rson]);g[x]=min(g[lson],g[rson]); } void Insert(int x,int l,int r,int k,int val) { if (l==r) {g[x]=val+t[x];f[x]=val;return;} int mid=l+r>>1; Pushdown(x); if (k<=mid) Insert(lson,l,mid,k,val); else Insert(rson,mid+1,r,k,val); f[x]=min(f[lson],f[rson]);g[x]=min(g[lson],g[rson]); } int Get(int x,int l,int r,int ll,int rr) { if (ll<=l&&r<=rr) return t[x]; int mid=l+r>>1,ans=0; Pushdown(x); if (ll<=mid) ans=Get(lson,l,mid,ll,rr); if (mid<rr) ans=max(ans,Get(rson,mid+1,r,ll,rr)); return ans; } int Query(int l,int r,int k) { int mid,ans=r+1,ub=r; while (l<=r) { mid=l+r>>1; if (Get(1,1,n,mid,ub)<k) ans=mid,r=mid-1; else l=mid+1; } return ans; } L Query(int x,int l,int r,int ll,int rr) { if (ll<=l&&r<=rr) return g[x]; int mid=l+r>>1; L ans=Inf; Pushdown(x); if (ll<=mid) ans=Query(lson,l,mid,ll,rr); if (mid<rr) ans=min(ans,Query(rson,mid+1,r,ll,rr)); return ans; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&h[i]); Change(1,1,n,1,1,h[1]); for (int i=1,s=0,j=1;i<=n;i++) { s+=h[i];while (s>m) s-=h[j++]; Change(1,1,n,Query(j,i-1,h[i]),i,h[i]); if (i==n) return printf("%lld",Query(1,1,n,j,i)),0; Change(1,1,n,i+1,i+1,h[i+1]);Insert(1,1,n,i+1,Query(1,1,n,j,i)); } }
View Code