书架

题目描述

由于最近又购买了很多书,所以你打算在自己的书房做一个新书架,但是由于墙体高度有限,并且为了照顾整体效果,你希望你的书架的宽度越小越好,所谓宽度就是指书架在垂直方向上占据的距离。

现按一定顺序给出所有要放置于书架上的书,要求求书架的最小宽度。每本书都有一个长度,而书架是「目」字形的,一层的宽度不能小于其上所摆放的任何一本书的长度。每本书的重量和它的长度成正比,而每层书架都有同样的最大承重,简单起见,换算成长度单位,记为m,也就是说每层上的书的长度之和不得超过m。整个书架的宽度为其上所有层的宽度之和。为了便于查找,任何层上的书必须为给出的书中的连续几本。

输入输出格式

输入格式:
第一行有两个正整数n、m。接下来有n行,每行一个正整数hi,i从1到n,hi ≤ m。

输出格式:
共一行,一个整数表示书架的最小宽度。

输入输出样例
输入样例#1: 复制
4 6
1
3
3
1
输出样例#1: 复制
5

说明
对于30%的数据,n ≤ 1,000
对于100%的数据,n ≤ 100,000,hi ≤ 10,000,m ≤ 1,000,000,000
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define imin(a,b) ((a<b)?(a):(b))
#define imax(a,b) ((a>b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=100100;
const ll inf=1e9;
const int mp=17;
int n,m;
int d[N],la[N];
int l,r;
ll sum[N],f[N],qx[N<<1],qy[N<<1];
ll h1[N],h2[N];
int cnt1,cnt2;
void add1(ll x) {
    cnt1++; h1[cnt1]=x;    int now=cnt1;
    while((now>1) && (h1[now>>1]>h1[now])) { swap(h1[now>>1],h1[now]); now>>=1;    }}
void down1() {
    h1[1]=h1[cnt1]; cnt1--;    int now=1;
    while((now<<1)<=cnt1) {
        int uw;    if((now<<1|1)>cnt1) uw=now<<1; else uw=(h1[now<<1]<h1[now<<1|1])?(now<<1):(now<<1|1);
        if(h1[now]>h1[uw]) swap(h1[now],h1[uw]); else break; now=uw; }}
void add2(int x) {
    cnt2++; h2[cnt2]=x;    int now=cnt2;
    while((now>1) && (h2[now>>1]>h2[now])) { swap(h2[now>>1],h2[now]); now>>=1; }}
void down2() {
    h2[1]=h2[cnt2]; cnt2--;    int now=1;
    while((now<<1)<=cnt2) {
        int uw; if((now<<1|1)>cnt2) uw=now<<1; else uw=(h2[now<<1]<h2[now<<1|1])?(now<<1):(now<<1|1);
        if(h2[now]>h2[uw]) swap(h2[now],h2[uw]); else break; now=uw; }}
ll getans()
{
    while(cnt2 && h1[1]==h2[1]) down1(),down2();
    return h1[1];
}
void read(int &x)
{
    x=0; char ch=getchar();
    while(ch<'0' && ch>'9') ch=getchar();
    while(ch>='0' && ch<='9')
    {
        x=(x<<3)+(x<<1)+ch-'0';
        ch=getchar();
    }
}
int main()
{
    read(n); read(m); sum[0]=0ll;
    for(int i=1;i<=n;i++) read(d[i]),sum[i]=sum[i-1]+d[i];
    int la=1; l=N; r=N-1;
    for(int i=1;i<=n;i++)
    {
        while(sum[i]-sum[la-1]>m)
        {
            if(r>=l && qx[l]==la) add2(f[la-1]+qy[l]),l++;
            la++;
        }
        if(l>r || qx[l]!=la)
        {
            l--;
            if(la==i) qy[l]=d[i];
            else qy[l]=imax(qy[l],d[i]);
            qx[l]=la;
            add1(f[la-1]+qy[l]);
        }
        int p=i;
        while(r>=l && qy[r]<=d[i])
        {
            p=qx[r];
            add2(f[qx[r]-1]+qy[r]);
            r--;
        }
        qx[++r]=p; qy[r]=d[i];
        add1(f[p-1]+d[i]);
        f[i]=getans();
    }
    printf("%lld\n",f[n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值