TJOI2011书架(dp)

当农夫约翰闲的没事干的时候,他喜欢坐下来看书。多年过去,他已经收集了 N 本书 (1 <= N <= 100,000), 他想造一个新的书架来装所有书。

每本书 i 都有宽度 W(i) 和高度 H(i)。书需要按顺序添加到一组书架上;比如说,第一层架子应该包含书籍1 ... k,第二层架子应该以第k + 1本书开始,以下如此。每层架子的总宽度最大为L(1≤L≤1,000,000,000)。每层的高度等于该层上最高的书的高度,并且整个书架的高度是所有层的高度的总和,因为它们都垂直堆叠。

请帮助农夫约翰计算整个书架的最小可能高度。

有N(1 <= N <= 100000)本书,每本书有一个宽度W(i),高度H(i),(1 <= H(i) <= 1,000,000; 1 <= W(i) <= L)。

现在有足够多的书架,书架宽度最多是L (1 <= L <= 1,000,000,000),把书按顺序(先放1,再放2.....)放入书架。某个书架的高度是该书架中所放的最高的书的高度。

将所有书放入书架后,求所有书架的高度和的最小值?

Solution

一眼dp优化。

状态方程显然,dp[[i]=dp[j]+maxh(j~i).然后用线段树优化,区间取max,单点修改。

但答案是两部分构成的,区间取max会炸。。

于是我想了一晚上。。。

我们发现每次取max会改变一段连续的区间,所以我们维护一个单调递减的队列,每次找到前面第一个比它大的点,直接区间修改就可以了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100002
using namespace std;
typedef long long ll;
long long tr[N<<2],sum[N],w[N],m,n,h[N],x,q[N],la[N<<2],ans[N<<2];
inline void pushdown(int cnt){
    la[cnt<<1]=la[cnt<<1|1]=la[cnt];la[cnt]=0; 
    ans[cnt<<1]=tr[cnt<<1]+la[cnt<<1];ans[cnt<<1|1]=tr[cnt<<1|1]+la[cnt<<1|1];
}
void add(int cnt,int l,int r,int x,ll y){
    if(l==r){
        tr[cnt]=y;
        ans[cnt]=y+la[cnt];
        return;
    }
    int mid=(l+r)>>1;
    if(la[cnt])pushdown(cnt);
    if(mid>=x)add(cnt<<1,l,mid,x,y);
    else add(cnt<<1|1,mid+1,r,x,y);
    tr[cnt]=min(tr[cnt<<1],tr[cnt<<1|1]);
    ans[cnt]=min(ans[cnt<<1],ans[cnt<<1|1]);
}
ll query(int cnt,int l,int r,int L,int R){ 
    if(l>=L&&r<=R)return ans[cnt];
    int mid=(l+r)>>1;
    ll ans=1e18;
    if(la[cnt])pushdown(cnt);
    if(mid>=L)ans=min(ans,query(cnt<<1,l,mid,L,R));
    if(mid<R)ans=min(ans,query(cnt<<1|1,mid+1,r,L,R));
    return ans;
}
void upd(int cnt,int l,int r,int L,int R,ll x){
    if(L>R)return;
    if(l>=L&&r<=R){
        la[cnt]=x;
        ans[cnt]=tr[cnt]+la[cnt];
        return;
    } 
    int mid=(l+r)>>1;
    if(la[cnt])pushdown(cnt);
    if(mid>=L)upd(cnt<<1,l,mid,L,R,x);
    if(mid<R)upd(cnt<<1|1,mid+1,r,L,R,x);
    tr[cnt]=min(tr[cnt<<1],tr[cnt<<1|1]);
    ans[cnt]=min(ans[cnt<<1],ans[cnt<<1|1]);
}
int find(int x){
    int l=0,r=x,ans;
    while(l<=r){
        int mid=(l+r)>>1;
        if(sum[x]-sum[mid]<=m){
            ans=mid;
            r=mid-1;
        } 
        else l=mid+1;
    }
    return ans;
} 
int main(){
    scanf("%lld%lld",&n,&m);
    memset(tr,0x3f,sizeof(tr));memset(ans,0x3f,sizeof(ans));
    for(int i=1;i<=n;++i)scanf("%lld%lld",&h[i],&w[i]),sum[i]=sum[i-1]+w[i];
    int mn=0;
    add(1,0,n,0,0);
    int hh,t;
    hh=t=1;q[hh]=0;
    for(int i=1;i<=n;++i){
        while(hh<=t&&h[q[t]]<h[i])t--;
        upd(1,0,n,hh<=t?q[t]:0,i-1,h[i]);
        q[++t]=i;
        x=query(1,0,n,find(i),i-1);
        add(1,0,n,i,x);
    }
    printf("%lld",x);
    return 0;
}

 

posted @ 2018-09-29 15:19  comld  阅读(518)  评论(0编辑  收藏  举报