[USACO12OPEN]Bookshelf G

LXXVIII.[USACO12OPEN]Bookshelf G

转移很简单,直接设\(f[i]\)表示前\(i\)个位置书架的最小高度和即可。

考虑转移。

我们有暴力的公式

\[f[i]=\min\limits_{j=1}^{i}\Big\{f_{j-1}+\max\{h_j,\dots,h_i\}\Big\} \]

因为当\(i\)不变时,随着\(j\)的增长,那个\(\max\)是单调不升的。因此我们可以用单调队列维护\(\max\)的转折点,因为\(\max\)一定是由一段一段组成的。

因为一整段里面的\(\max\)都是一样的,因此它就只取决于那个\(f_j\)。因此我们只需要对于每一段维护一个\(\min\{f_j\}\)即可。

法1.线段树

这个\(f_j\)当然可以用线段树求区间\(\min\)。因为单调队列中,只有队首和队尾的两个元素的\(\min\{f_j\}+h_k\)是每次都要重算的,因此总复杂度\(O(n\log n)\)。用std::multiset维护单调队列中\(\min\{f_j\}+h_k\)的值,在插入新元素时重算后投入multiset,弹出元素时扔出去即可。总复杂度\(O(n\log n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
#define int long long
int n,m,s[100100],h[100100],f[100100],q[100100],p[100100],l,r,mn[400100];
void pushup(int x){
	mn[x]=min(mn[lson],mn[rson]);
}
void Modify(int x,int l,int r,int P,int val){
	if(l>P||r<P)return;
	if(l==r){mn[x]=val;return;}
	Modify(lson,l,mid,P,val),Modify(rson,mid+1,r,P,val),pushup(x);
}
int Ask(int x,int l,int r,int L,int R){
	if(l>R||r<L)return 0x3f3f3f3f3f3f3f3f;
	if(L<=l&&r<=R)return mn[x];
	return min(Ask(lson,l,mid,L,R),Ask(rson,mid+1,r,L,R));
}
multiset<int>ms;
signed main(){
	scanf("%lld%lld",&n,&m),memset(f,0x3f,sizeof(f)),f[0]=0;
	for(int i=1;i<=n;i++)scanf("%lld%lld",&h[i],&s[i]),s[i]+=s[i-1];
	l=r=1,ms.insert(0);
	for(int i=1,j=1;i<=n;i++){
		Modify(1,1,n,i,f[i-1]);
		while(s[i]-s[j-1]>m)j++;
		while(l<=r&&s[i]-s[q[l]-1]>m)ms.erase(ms.find(p[l++]));
		while(l<=r&&h[i]>=h[q[r]])ms.erase(ms.find(p[r--]));
		q[++r]=i;
		if(l<r)p[r]=h[q[r]]+Ask(1,1,n,q[r-1]+1,q[r]),ms.erase(ms.find(p[l])),ms.insert(p[r]);
		p[l]=h[q[l]]+Ask(1,1,n,j,q[l]),ms.insert(p[l]);
		f[i]=*ms.begin();
	}
	printf("%lld\n",f[n]);
	return 0;
}

法2.直接观察

实际上,这个\(f\)是单调不降的(注意它的定义)。因此\(\min\{f_j\}\)一定出现在最左边的地方。因此直接用最左边的\(f\)值替换上面的\(\min\)即可。因为复杂度瓶颈在multiset上,因此复杂度不变。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,s[100100],h[100100],f[100100],q[100100],p[100100],l,r;
multiset<int>ms;
signed main(){
	scanf("%lld%lld",&n,&m),memset(f,0x3f,sizeof(f)),f[0]=0;
	for(int i=1;i<=n;i++)scanf("%lld%lld",&h[i],&s[i]),s[i]+=s[i-1];
	l=r=1,ms.insert(0);
	for(int i=1,j=1;i<=n;i++){
		while(s[i]-s[j-1]>m)j++;
		while(l<=r&&s[i]-s[q[l]-1]>m)ms.erase(ms.find(p[l++]));
		while(l<=r&&h[i]>=h[q[r]])ms.erase(ms.find(p[r--]));
		q[++r]=i;
		if(l<r)p[r]=h[q[r]]+f[q[r-1]],ms.erase(ms.find(p[l])),ms.insert(p[r]);
		p[l]=h[q[l]]+f[j-1],ms.insert(p[l]);
		f[i]=*ms.begin();
	}
	printf("%lld\n",f[n]);
	return 0;
}

原文地址:https://www.cnblogs.com/Troverld/p/14598442.html