zoukankan      html  css  js  c++  java
  • BZOJ 1911 特别行动队 (斜率优化)

    $ BZOJ~1911~*~ $ 特别行动队: (斜率优化)



    $ solution: $

    感觉这道题目还是比较常规的,首先我们很容易想到DP,因为题目里面说了选出的人都是连续的,这意味着我们可以从前往后DP。我们直接设 (f[i]) 表示前 (i) 在分组之后的战斗力之和(因为题目没有明确要求分几组,所以我们省去这一维度)。然后转移也比较常规,我们枚举前面的某一个人 (k) 作为这一组人的左端点(右端点就是第 (i) 个人)。这题数据范围很大,我们也不用着急,现将式子列出来,如果转移不能优化再另寻他法。(事实上这种没有性质的DP,优化只能靠转移方程)

    转移方程:

    $ F[i]=F[k]+a imes (S[i]-S[k])^2+b imes(S[i]-S[k])+c $

    把这个式子拆开你就会看到一个与 (i,k) 都有关的乘积项,这是斜率优化的标志:

    $ F[i] = F[k] + a imes S[i]^2 - 2a imes S[i] imes S[k] + a imes S[k]^2+b imes S[i] - b imes S[k] + c $

    然后我们将同一类的项移动到一起,化为斜率优化的标准式:

    $ F[k] + a imes S[k]^2 - b imes S[k] = + 2a imes S[i] imes S[k] + F[i] -a imes S[i]^2 - b imes S[i] - c $

    然后我们将 乘积项(2a imes S[i] imes S[k]) 里面有关于 (k)(S[k]) 作为横坐标(自变量) ,然后等式右边有关 (k) 的项 (F[k] + a imes S[k]^2 - b imes S[k]) 作为纵坐标(应变量)。然后我们要 (F[i]) 最大,其实就是让截距 (F[i] -a imes S[i]^2 - b imes S[i] - c) 最大。于是直接斜率优化即可。



    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    
    #define ll long long
    #define db double
    #define rg register int
    
    using namespace std;
    
    int n;
    ll a,b,c;
    int s[1000005];
    int q[1000005];
    ll g[1000005];
    ll f[1000005];
    
    inline int qr(){
    	register char ch; register bool sign=0; rg res=0;
    	while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    	while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
    	if(sign)return -res; else return res;
    }
    
    int main(){
    	//freopen(".in","r",stdin);
    	//freopen(".out","w",stdout);
    	n=qr(); a=qr(); b=qr(); c=qr(); rg l=1,r=0;
    	for(rg i=1;i<=n;++i) s[i]=s[i-1]+qr();
    	for(rg i=1;i<=n;++i){  q[++r]=i-1;
    		while(l<r&&g[q[l+1]]-g[q[l]]>=2*a*s[i]*(s[q[l+1]]-s[q[l]]))++l;
    		rg x=s[i]-s[q[l]];
    		f[i]=f[q[l]]+a*x*x+b*x+c;
    		g[i]=f[i]+a*s[i]*s[i]-b*s[i];
    		while(l<r&&(g[q[r]]-g[q[r-1]])*(s[i]-s[q[r]])<=(g[i]-g[q[r]])*(s[q[r]]-s[q[r-1]]))--r;
    	}printf("%lld
    ",f[n]);
    	return 0;
    }
    
    
  • 相关阅读:
    Linux培训教程lgzip命令详解和使用实例
    Linux 新手应该知道的一些求助命令
    “变态教育创导者”兄弟连教育新三板挂牌上市
    linux中more命令如何使用
    html上标与下标应用
    linux命令大全之cal命令详解(显示日历)
    成为java高手的八大条件
    mysql 1055
    MySQL更改口令报错ERROR 1064
    centos7.5 安装mysql8.0
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/11213565.html
Copyright © 2011-2022 走看看