书架 bookshelf
书架 bookshelf
题目描述
当Farmer John闲下来的时候,他喜欢坐下来读一本好书。 多年来,他已经收集了N本书 (1 <= N <= 100,000)。 他想要建立一个多层书架,来存放它们。
每本书 i 拥有一个宽度 W(i)和一个高度 H(i)。 所有的书需要按顺序,放到书架的每一层。 举例来说,第一层书架放k本书,应该放书1...k;第二层书架从第k+1本书开始放……。 每层书架的宽度最多为L (1 <= L <= 1,000,000,000)。 每层书架的高度为该层最高的那本书的高度。 书架的总高度为每层书架高度的和。
请帮FJ计算书架可能的最小总高度。
输入
第1行:两个空格隔开的整数:N和L
第2行..第N+1行:第i+1行包含两个空格隔开的整数:H(i) 和 W(i) 。(1 <= H(i) <= 1,000,000; 1 <= W(i) <= L)。
输出
仅一行,表示书架可能的最小总高度
样例输入
5 10 5 7 9 2 8 5 13 2 3 8
样例输出
21
提示
【样例解释】
一共有5本书。每层书架的宽度最多为10。
3层书架,第1层放书1(高5,宽7),第2层放书2..4(高13,宽9),第3层放书5(高3,宽8)。
【数据范围】
45%的数据:1<=N<=5,000
100%的数据:1<=N<=100,000,1<=Wi<=L<=1,000,000,000,1<=Hi<=1,000,000
solution
考虑暴力dp
f[i]表示放好前i本书的最小高度。
f[i]=min (f[j]+max(h[j+1]--h[i])) (w[i]-w[j]<=L)
效率O(n^2)
现在优化时间复杂度。
枚举右端点r,我们维护最靠前的可转移位置l。
也就是我要知道[l,r]内f[i]+maxh[i~r]的最小值
由于f不变,h可能变(而且是整段改变),我们用线段树维护。
f相当于单点加,而h则是区间修改。
分别维护f的最小值和f+h的最小值,改变h时用旧的f与新的h更新f+h即可。
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define maxn 100005 #define inf 1e16 #define ll long long using namespace std; int n,id[maxn],top; ll w[maxn],h[maxn],zh[maxn],len,fn; struct node{ ll f,bj,mi; }tr[maxn*4]; void update(int k,ll v){ tr[k].bj=v;tr[k].mi=tr[k].f+v; } void down(int k){ if(tr[k].bj){ ll &v=tr[k].bj; update(k*2,v);update(k*2+1,v); v=0; } } void wh(int k){ tr[k].f=min(tr[k*2].f,tr[k*2+1].f); tr[k].mi=min(tr[k*2].mi,tr[k*2+1].mi); } void build(int k,int l,int r){ if(l==r){ if(l) tr[k].f=tr[k].mi=inf; return; } int mid=l+r>>1; build(k*2,l,mid);build(k*2+1,mid+1,r); wh(k); } void ch(int k,int l,int r,int li,int ri,ll v){ if(l>=li&&r<=ri){ update(k,v);return; } down(k); int mid=l+r>>1; if(li<=mid)ch(k*2,l,mid,li,ri,v); if(ri>mid)ch(k*2+1,mid+1,r,li,ri,v); wh(k); } ll ask(int k,int l,int r,int li,int ri){ if(l>=li&&r<=ri){ return tr[k].mi; } down(k); int mid=l+r>>1; ll tmp=inf; if(li<=mid)tmp=min(tmp,ask(k*2,l,mid,li,ri)); if(ri>mid)tmp=min(tmp,ask(k*2+1,mid+1,r,li,ri)); return tmp; } void add(int k,int l,int r,int pl,ll v){ if(l==r){ tr[k].f=v;return; } down(k); int mid=l+r>>1; if(pl<=mid)add(k*2,l,mid,pl,v); else add(k*2+1,mid+1,r,pl,v); wh(k); } int main(){ cin>>n>>len; for(int i=1;i<=n;i++){ scanf("%lld%lld",&h[i],&w[i]); w[i]+=w[i-1]; } int la=0; build(1,0,n); for(int i=1;i<=n;i++){ while(top&&h[i]>zh[top])top--; //cout<<"ch "<<id[top]<<' '<<i-1<<endl; ch(1,0,n,id[top],i-1,h[i]); while(w[i]-w[la]>len)la++; //cout<<"la "<<la<<endl; fn=ask(1,0,n,la,i-1); //cout<<fn<<endl; add(1,0,n,i,fn); zh[++top]=h[i];id[top]=i; } cout<<fn<<endl; return 0; }