zoukankan      html  css  js  c++  java
  • BZOJ-5424: 烧桥计划(单调队列)

    BZOJ-5424: 烧桥计划(单调队列)

    题目链接

    题解:

    先考虑最暴力的(dp):设(f[k][i])表示搞掉第(1sim i)段,烧了(k)段的最小花费,设(calc(x,y)=sum[xsim y]le M?0:sum[xsim y]),可以列出转移方程如下

    [f[k][i]=min(f[k-1][j]+calc(j+1,i))+k*a[i] (j<i) ]

    这样时间复杂度是(O(n^3))的,十分爆炸

    考虑优化
    首先发现题目中给出的(1000 le a[i]le 2000)。仔细想想,这表明(k)值最大不会太大
    设最坏情况下取了(k),则此时一定是满足(k*(k+1)/2*1000le n*2000)
    (就是说不是你把(n)段桥都断了也比(k)段优)
    这样算下来(n)最大的时候(k)也就是(600)的样子,(O(n*k))就可以过了

    但现在时间复杂度还是(O(n^2k))的,考虑对于每个(k)的每个(i),如何快速计算此时的(f[k][i])

    这个时候就可以用单调队列优化了。
    设当前是(f[k][i]),题目中(M)的限制(就是那个(calc(x,y)))就相当于把(1sim i)段分成了两部分:

    前半部分要计算中间的(sum[xsim y]),后半部分不用
    那么对于前半部分记一个最小值,后半部分维护递增的单调队列,(dp)时取两个最小的那个就可以做到(O(1))转移了

    细节不少,刚开始写感觉很迷,写着写着也就想明白了吧
    注意(k)是没有单调性的,一定从(1)(T)全枚举一遍

    (sxz)一起卡了波时间,惊奇地发现(k)最大居然只有(152)
    (别问为什么这么准,二分试出来的)

    代码:

    #include<map>
    #include<set>
    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define qmax(x,y) (x=max(x,y))
    #define qmin(x,y) (x=min(x,y))
    #define mp(x,y) make_pair(x,y)
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    inline int read(){
    	int ans=0,fh=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    		ans=ans*10+ch-'0',ch=getchar();
    	return ans*fh;
    }
    const int maxn=1e5+100;
    int n,m,mx,q[maxn],p[maxn],l,r,a[maxn],f[2][maxn];
    int tot,mn,sum[maxn],Ans=0x7fffffff;
    int main(){
    //	freopen("nh.in","r",stdin);
    //	freopen("zhy.out","w",stdout);
    	n=read(),m=read();
    	for(int i=1;i<=n;i++) a[i]=read();a[++n]=0;
    	for(int i=1;i<=n;i++) sum[i]=a[i]+sum[i-1];
    	int k=0,o=0,lc;
    	memset(f,0x3f,sizeof(f));
    	f[0][0]=f[1][0]=0;
    	int T=min(152,(int)sqrt(n*4));
    	while(T--){
    		l=1,r=1,k++,tot=0,mn=0,lc=0;
    		o^=1,q[1]=p[1]=0;
    		for(int i=1;i<=n;i++){
    			int now=0x7fffffff;
    			while(l<=r&&sum[i-1]-sum[q[l]]>m) l++;
    			while(1){
    				if(sum[i-1]-sum[lc]<=m) break;
    				qmin(mn,f[o^1][lc]+sum[i-1]-sum[lc]-tot);
    				lc++;
    			}
    			if(l<=r) qmin(now,p[l]);
    			qmin(now,mn+tot);
    			f[o][i]=(now+=k*a[i]);
    			while(l<=r&&p[r]>=f[o^1][i]) r--;
    			p[++r]=f[o^1][i],q[r]=i;
    			tot+=a[i];
    		}
    		qmin(Ans,f[o][n]);
    	}
    	printf("%d
    ",Ans);
    	return 0;
    }
    
  • 相关阅读:
    lnmp环境搭建
    Git常用命令
    博客园写随笔环境搭建
    Win常用软件
    Docker环境搭建
    ESP-8266 RTOS 环境搭建
    查看Linux信息
    博客园markdown语法
    Java后台技术(TDDL)
    Java后台技术(Dubbo入门)
  • 原文地址:https://www.cnblogs.com/nianheng/p/10502570.html
Copyright © 2011-2022 走看看