jzoj2931 书架

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 false
using 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值