题解 P1848 [USACO12OPEN]Bookshelf G
题解
设 \(f_i\) 表示前 \(i\) 本书满足条件时书架高度和的最小值。
那么对于当前位置 \(i\),满足宽度限制的最小位置 \(w\) 一定不减,可以用指针维护。
显然有 \(f_i=\min\{f_{j-1}+\max\{H_j,\cdots,H_i\}\}(w\le j\le i)\)。
我们把原来的 \(H_j\) 变为 \(H_j\sim H_i\) 的最大值,显然要动态维护 \(H_w\sim H_i\) 的值,只需支持对 \(H\) 后缀取 \(\max\) 操作。
由于 \(H\) 的最大值是不增的,所以取 \(\max\) 相当于对一段后缀做区间覆盖。
线段树上二分找到当前要取 \(\max\) 的分界点,剩下的前缀不会被影响。
我们还需维护区间内 \(f_i\) 的 \(\min\) 和 \(f_i+H_i\) 的 \(\min\),以及查询 \(w\sim i\) 中最小的 \(f_i+H_i\)。
这都可以用线段树来维护。时间复杂度 \(O(n\log n)\)。
Code
#include<cstdio>
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3f
int n,l,w=1,sum=0;
int H[100008],W[100008];
LL f[100008];
struct aaa
{
LL mnf,mnfh,laz,mnh;
}arr[400008];
inline LL max(LL a,LL b)
{
return a>b? a:b;
}
inline LL min(LL a,LL b)
{
return a<b? a:b;
}
inline int lson(int x)
{
return (x<<1);
}
inline int rson(int x)
{
return ((x<<1)|1);
}
inline void build(int k,int l,int r)
{
if(!l)arr[k]=(aaa){0,0,0,0};
else arr[k]=(aaa){inf,inf,0,0};
if(l==r)return ;
int ls=lson(k),rs=rson(k),mid=((l+r)>>1);
build(ls,l,mid),build(rs,mid+1,r);
}
inline void modifyH(int k,int l,int r,int l1,int r1,LL d)
{
if(l1>r1)return ;
if(l>=l1 && r<=r1){arr[k].laz=d,arr[k].mnh=d,arr[k].mnfh=arr[k].mnf+d;return ;}
if(arr[k].laz)arr[k].mnh=arr[k].laz,arr[k].mnfh=arr[k].mnf+arr[k].mnh;
int ls=lson(k),rs=rson(k),mid=((l+r)>>1);
if(arr[k].laz)
{
arr[ls].laz=arr[k].laz,arr[ls].mnfh=arr[ls].mnf+arr[ls].laz,arr[ls].mnh=arr[ls].laz;
arr[rs].laz=arr[k].laz,arr[rs].mnfh=arr[rs].mnf+arr[rs].laz,arr[rs].mnh=arr[rs].laz,arr[k].laz=0;
}
if(r1<=mid)modifyH(ls,l,mid,l1,r1,d);
else if(l1>mid)modifyH(rs,mid+1,r,l1,r1,d);
else modifyH(ls,l,mid,l1,mid,d),modifyH(rs,mid+1,r,mid+1,r1,d);
arr[k].mnfh=min(arr[ls].mnfh,arr[rs].mnfh),arr[k].mnh=min(arr[ls].mnh,arr[rs].mnh);
}
inline LL query(int k,int l,int r,int l1,int r1)
{
if(arr[k].laz)arr[k].mnh=arr[k].laz,arr[k].mnfh=arr[k].mnf+arr[k].mnh;
if(l>=l1 && r<=r1)return arr[k].mnfh;
int ls=lson(k),rs=rson(k),mid=((l+r)>>1);
if(arr[k].laz)
{
arr[ls].laz=arr[k].laz,arr[ls].mnfh=arr[ls].mnf+arr[ls].laz,arr[ls].mnh=arr[ls].laz;
arr[rs].laz=arr[k].laz,arr[rs].mnfh=arr[rs].mnf+arr[rs].laz,arr[rs].mnh=arr[rs].laz,arr[k].laz=0;
}
if(r1<=mid)return query(ls,l,mid,l1,r1);
if(l1>mid)return query(rs,mid+1,r,l1,r1);
return min(query(ls,l,mid,l1,mid),query(rs,mid+1,r,mid+1,r1));
}
inline void modifyF(int k,int l,int r,int x,LL d)
{
if(arr[k].laz)arr[k].mnh=arr[k].laz,arr[k].mnfh=arr[k].mnf+arr[k].mnh;
if(l==r){arr[k].mnf=d,arr[k].mnfh=d+arr[k].mnh;return ;}
int ls=lson(k),rs=rson(k),mid=((l+r)>>1);
if(arr[k].laz)
{
arr[ls].laz=arr[k].laz,arr[ls].mnfh=arr[ls].mnf+arr[ls].laz,arr[ls].mnh=arr[ls].laz;
arr[rs].laz=arr[k].laz,arr[rs].mnfh=arr[rs].mnf+arr[rs].laz,arr[rs].mnh=arr[rs].laz,arr[k].laz=0;
}
if(x<=mid)modifyF(ls,l,mid,x,d);else modifyF(rs,mid+1,r,x,d);
arr[k].mnf=min(arr[ls].mnf,arr[rs].mnf),arr[k].mnfh=min(arr[ls].mnfh,arr[rs].mnfh),arr[k].mnh=max(arr[ls].mnh,arr[rs].mnh);
}
inline int find(int k,int l,int r,int l1,int r1,LL d)
{
if(arr[k].laz)arr[k].mnh=arr[k].laz,arr[k].mnfh=arr[k].mnf+arr[k].mnh;
if(l==r)return (d>arr[k].mnh? l:n);
int ls=lson(k),rs=rson(k),mid=((l+r)>>1);
if(arr[k].laz)
{
arr[ls].laz=arr[k].laz,arr[ls].mnfh=arr[ls].mnf+arr[ls].laz,arr[ls].mnh=arr[ls].laz;
arr[rs].laz=arr[k].laz,arr[rs].mnfh=arr[rs].mnf+arr[rs].laz,arr[rs].mnh=arr[rs].laz,arr[k].laz=0;
}
if(r1>mid && d<=max(arr[ls].mnh,arr[ls].laz))return find(rs,mid+1,r,max(mid+1,l1),r1,d);
return find(ls,l,mid,l1,min(mid,r1),d);
}
int main()
{
scanf("%d%d",&n,&l);
for(int i=1;i<=n;++i)scanf("%d%d",&H[i],&W[i]);
build(1,0,n);
for(int i=1;i<=n;++i)
{
for(sum+=W[i];sum>l;++w)sum-=W[w];
modifyH(1,0,n,find(1,0,n,0,i-1,H[i]),i-1,H[i]),f[i]=query(1,0,n,w-1,i-1),modifyF(1,0,n,i,f[i]);
}
printf("%lld",f[n]);
return 0;
}