BZOJ4268 : 小强的书架
首先将所有高度乘上10,设f[i]为将前i本书放入书架的最小高度,则
\[\begin{eqnarray*}
f[i]&=&\min(f[j-1]+first(j,i)+second(j,i)+W-(s[i]-s[j-1]))\\
&=&\min(f[j-1]+first(j,i)+second(j,i)+W-s[i]+s[j-1])\\
&=&\min(f[j-1]+first(j,i)+second(j,i)+s[j-1])+W-s[i]
\end{eqnarray*}\]
其中$1\leq j\leq i$且$s[i]-s[j-1]\leq W$。
设tmp[i][j]=f[j-1]+s[j-1]+first(j,i)+second(j,i),考虑用线段树维护tmp[i][j],如果当前要计算f[i],那么线段树的第j个叶子节点就表示tmp[i][j]。
为了高效维护first和second,先将所有书按高度排序,从大到小插入到set中。
假设现在插入的是第x本书,那么对于[pre(x)+1,x]内的i,first(i,x)均为a[x]。
对于[pre(pre(x))+1,pre(x)]内的i,以及[x,nxt(x)-1]内的j,second(i,j)均为a[x]。
对于[pre(x)+1,x]内的i,以及[nxt(x),nxt(nxt(x))-1]内的j,second(i,j)也均为a[x]。
于是在右端点的最小值处进行区间赋值操作即可。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<algorithm> #include<set> #define N 300010 using namespace std; typedef long long ll; int n,w,i,j,k,c[N],l[N],g[N],vl[N<<1],vr[N<<1],nxt[N<<1],ed;ll a[N],b[N],vw[N<<1],f; set<int>T;set<int>::iterator it; inline bool cmp(int x,int y){return a[x]<a[y];} inline int get(){char c;while(!(((c=getchar())>='0')&&(c<='9')));int a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';return a;} inline void add(int x,int l,int r,ll w){vl[++ed]=l;vr[ed]=r;vw[ed]=w;nxt[ed]=g[x];g[x]=ed;} struct node{ll v1,v2,vc,v,t1,t2;}S[1050000]; inline void tag1(int x,ll p){ S[x].v1=p+S[x].vc; S[x].v=p+S[x].v2; S[x].t1=p; } inline void tag2(int x,ll p){ S[x].v2=p+S[x].vc; S[x].v=p+S[x].v1; S[x].t2=p; } inline void pb(int x){ if(S[x].t1){ tag1(x<<1,S[x].t1); tag1(x<<1|1,S[x].t1); S[x].t1=0; } if(S[x].t2){ tag2(x<<1,S[x].t2); tag2(x<<1|1,S[x].t2); S[x].t2=0; } } inline void up(int x){ S[x].v1=min(S[x<<1].v1,S[x<<1|1].v1); S[x].v2=min(S[x<<1].v2,S[x<<1|1].v2); S[x].vc=min(S[x<<1].vc,S[x<<1|1].vc); S[x].v=min(S[x<<1].v,S[x<<1|1].v); } void same1(int x,int a,int b,int c,int d,ll p){ if(c<=a&&b<=d){tag1(x,p);return;} pb(x); int mid=(a+b)>>1; if(c<=mid)same1(x<<1,a,mid,c,d,p); if(d>mid)same1(x<<1|1,mid+1,b,c,d,p); up(x); } void same2(int x,int a,int b,int c,int d,ll p){ if(c<=a&&b<=d){tag2(x,p);return;} pb(x); int mid=(a+b)>>1; if(c<=mid)same2(x<<1,a,mid,c,d,p); if(d>mid)same2(x<<1|1,mid+1,b,c,d,p); up(x); } void change(int x,int a,int b,int c,ll p){ if(a==b){ S[x].vc=p; S[x].v1=p+S[x].t1; S[x].v2=p+S[x].t2; S[x].v=p+S[x].t1+S[x].t2; return; } pb(x); int mid=(a+b)>>1; c<=mid?change(x<<1,a,mid,c,p):change(x<<1|1,mid+1,b,c,p); up(x); } ll ask(int x,int a,int b,int c,int d){ if(c<=a&&b<=d)return S[x].v; pb(x); int mid=(a+b)>>1;ll t=1LL<<60; if(c<=mid)t=ask(x<<1,a,mid,c,d); if(d>mid)t=min(t,ask(x<<1|1,mid+1,b,c,d)); return up(x),t; } int main(){ n=get(),w=get(); for(i=1;i<=n;i++)a[i]=10LL*get(),b[i]=b[i-1]+get(),c[i]=i; for(sort(c+1,c+n+1,cmp),T.insert(0),T.insert(n+1),i=n;i;i--){ T.insert(c[i]); it=T.find(c[i]); it--,j=*it,l[c[i]]=*it+1; if(j)it--,add(c[i],*it+1,j,a[c[i]]); it=T.find(c[i]); it++,j=*it; if(j<=n)add(j,l[c[i]],c[i],a[c[i]]); } for(i=j=1;i<=n;i++){ same1(1,1,n,l[i],i,a[i]); same2(1,1,n,i,i,a[i]); for(k=g[i];k;k=nxt[k])same2(1,1,n,vl[k],vr[k],vw[k]); while(b[i]-b[j-1]>w)j++; f=ask(1,1,n,j,i)+w-b[i]; if(i<n)change(1,1,n,i+1,f+b[i]); } return printf("%lld.%d",f/10,f%10),0; }