• bzoj1911[Apio2010] 特别行动队


    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1911

    题目大意:

    有n个数,分成连续的若干段,每段的分数为a*x^2+b*x+c(a,b,c是给出的常数),其中x为该段的各个数的和。求如何分才能使得各个段的分数的总和最大。


    ===============================================

    题解:

    斜率优化 这个方程很好写啊

    设f[i]表示i为一个分段点且i之前的已经分好搞好了的最大值。

    sum[]为前缀和

    那么方程就是f[i]=f[j]+a*(sum[i]-sum[j-1])^2+b*(sum[i]-sum[j-1])+c

    拆了移项得:2*a*sum[i]*sum[j-1]+f[i]=f[j]+a*sum[j-1]^2-b*sum[j-1]+(a*sum[i]^2+b*sum[i]+c)

    虽然可能有点长..但是真的很好化(水)

    还有,求最大值,所以维护上凸包哦~


    代码~

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    #define maxn 1010000
    
    int q[maxn],l,r;
    LL f[maxn],sum[maxn],a,b,c;
    double Y(int j){return f[j]+a*sum[j]*sum[j]-b*sum[j];}
    double X(int j){return sum[j];}
    double slop(int j1,int j2){return (Y(j2)-Y(j1))/(X(j2)-X(j1));}
    int main()
    {
    	//freopen("a.in","r",stdin);
    	//freopen("a.out","w",stdout);
    	int n,i;scanf("%d",&n);
    	scanf("%lld%lld%lld",&a,&b,&c);
    	sum[0]=0;
    	for (i=1;i<=n;i++)
    	{
    		scanf("%lld",&sum[i]);
    		sum[i]+=sum[i-1];
    	}
    	memset(f,0,sizeof(f));
    	l=r=1;q[1]=0;
    	for (i=1;i<=n;i++)
    	{
    		while (l<r && slop(q[l],q[l+1])>2*a*sum[i]) l++;
    		int j=q[l];
    		f[i]=f[j]+a*(sum[i]-sum[j])*(sum[i]-sum[j])+b*(sum[i]-sum[j])+c;
    		while (l<r && slop(q[r-1],q[r])<slop(q[r],i)) r--;
    		q[++r]=i;
    	}printf("%lld
    ",f[n]);
    }


  • 相关阅读:
    【c语言】斐波那契数列
    【c语言】c语言中的问题--empty character constant
    【java 基础领域】类加载机制
    【书籍学习】汇编语言学习-第二章
    【专接本课程】c语言指针学习
    Balanced Binary Tree
    Symmetric Tree
    Same Tree
    Recover Binary Search Tree
    Binary Tree Zigzag Level Traversal
  • 原文地址:https://www.cnblogs.com/Euryale-Rose/p/6527865.html
Copyright © 2020-2023  润新知