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做法
其中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;
}