• [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;
    }
    

  • 相关阅读:
    Windows平台下MySQL常用操作与命令
    button和sumbit提交表单的区别
    AjaxPro.Net的使用
    小写金额转换成大写
    sqlserver自动定时备份数据库并按日期命名
    dw文档的当前编码不能正确保存文档所有字符.....
    windows 2003 ii6 上运行asp出现http500错误
    图片提示预览效果
    一个网站优化seo的年终工作总结
    很COOL的相册
  • 原文地址:https://www.cnblogs.com/Troverld/p/14598442.html
Copyright © 2020-2023  润新知