jzoj2931 书架

来源:互联网 发布:亚马逊的大数据行动 编辑:程序博客网 时间:2024/05/03 16:57

Description

当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计算书架可能的最小总高度。

简洁题意

给定相同长度序列a,b,要求划分段满足每一段的bi<=l,求min(max(al..r))

解法

最大值最小化这种神奇的问题,一般可以考虑两种方法
1.二分,转化为判定问题
2.DP只要是有max min这种东西 我们可以考察其单调性或可维护性,并加以数据结构维护最值s

这里由于是序列划分,所以我们选择第二种
fi表示放完i本书的最小代价
然后我们就得到了一个n^2的简单DP做法

fi=min(fj+maxhj+1)

其中maxh_j+1表示j+1~i的最大h
可以发现f与maxh的单调性(显然)
再设ok为当前i到ok内(包括)的宽度可以满足bi<=l
那这个时候我们的问题就是如何在ok-1~i-1这个区间内取j使得f[i]尽量小

我们选择种一颗线段树,维护区间中最小的maxh+f[最左点] 因为我们知道f满足单调不递减
每一次取答案,其实就是query(ok-1~i-1)
那如何维护呢
已知maxh的单调性,那么我们去找,k=maxhk~maxhi全部需要更新为h[i] (k<=i)
这个可以考虑二分和单调队列
找到这个k之后,我们就要区间修改线段树[k,i-1]的值
因为f[最左点]这个值是给定的不需要维护,所以我们就打一个lazy标记表示该区间所有点的maxh都替换为更新成的那个. 这样查询的时候顺手清理掉lazy标记就可以n log n过掉这一题了

恶心的地方

由于c++不报数组上溢
所以莫名其妙的单调队列被改
所以莫名奇妙的调了一天
所以还是机智的发现了
我真聪明(真不要脸)

CODE

#include <cstdio>#include <iostream>#include <cstring>#define MAXN 100100#define fo(i,j,k) for(int i=j;i<=k;i++)#define fd(i,j,k) for(int i=j;i>=k;i--)#define DEBUG falseusing namespace std;long long minHW,l,n,w[MAXN*5],h[MAXN*5],f[MAXN*5],pre[MAXN*5],t[MAXN*4*5],lazy[MAXN*4*5],ok,result,q[MAXN*5],perLoc[MAXN*5],head,tail;long min(long a,long b) {    return (a>b)?b:a;}void clearLazy(int x,int l,int r) {    //cout<<x<<" "<<leftLoc<<" "<<lazy[x]<<endl;    if (lazy[x]==0) return;    t[x]=f[l]+lazy[x];    t[x*2]=f[l]+lazy[x];    t[x*2+1]=f[x*2]+lazy[x];    lazy[x*2]=lazy[x*2+1]=lazy[x];    lazy[x]=0;}void update(int x) {    t[x]=min(t[x*2+1],t[x*2]);}int push(long long x,int itsLoc) {    while (tail>0 && q[tail]<x) {        perLoc[tail]=0;        tail--;    }    q[++tail]=x;    perLoc[tail]=itsLoc;    //if (tail<head) return perLoc[tail]; else    return perLoc[tail-1];}void headAdd() {    while (perLoc[head]<ok && head<=tail) head++;}void query(int x,int l,int r,int fl,int fr) {    if (l>fr || r<fl) return;    if (DEBUG) cout<<l<<" "<<r<<" "<<fl<<" "<<fr<<endl;    if (l>=fl && r<=fr) {        clearLazy(x,l,r);        result=(result<t[x])?result:t[x];        return;    }    clearLazy(x,l,r);    query(x*2,l,(l+r)>>1,fl,fr);    query(x*2+1,((l+r)>>1)+1,r,fl,fr);}void change(int x,int l,int r,int fl,int fr,long long v) {      if (l>fr || r<fl) return;    if (DEBUG) cout<<l<<" "<<r<<" "<<fl<<" "<<fr<<endl;    if (fl<=l && r<=fr ) {        if (DEBUG) cout<<lazy[x]<<"=>"<<v<<endl;        lazy[x]=v;        t[x]=f[l]+v;        //clearLazy(x,l,r);        return;    }    clearLazy(x,l,r);    change(x*2,  l,(l+r)>>1,fl,fr,v);    change(x*2+1,((l+r)>>1)+1,r,fl,fr,v);    update(x);}int main() {    cin>>n>>l;    fo(i,1,n)   {        scanf("%lld %lld",&h[i],&w[i]);        pre[i]=pre[i-1]+w[i];    }    //memset(f,127,sizeof f);    f[0]=0;    ok=1;    head=1;    tail=0;    perLoc[0]=-1;    fo(i,1,n) {        //if (i>=3) cout<<pre[i]<<endl;        perLoc[0]=-1;        while (pre[i]-pre[ok-1]>l) ok++;        int loc = push(h[i],i);        //if (i>=3) cout<<loc+1<<endl;        if (DEBUG) cout<<"change"<<endl;        change(1,0,n,loc,i-1,h[i]);        result=99999999999999999;        if (DEBUG) cout<<"query"<<endl;        query(1,0,n,ok-1,i-1);        if (result==99999999999999999) result=0;        f[i]=result;        //change(1,0,n,i,i);         if (DEBUG) cout<<f[i]<<endl;    }    cout<<f[n]<<endl;    return 0;}
1 0
原创粉丝点击