• BZOJ1911: [Apio2010]特别行动队


    【传送门:BZOJ1911


    简要题意:

      有n个人,每个人都有一个战力值,将这n个人分成若干个段(每个段内的人的编号都是连续的),每个段的初始战力值为每个段内的人的战力值的总和

      给出常数a,b,c,而每个段的真正战力值为ax2+bx+c(x为这个段的初始战力值),求出分成若干个段得到的所有段的最大真正战力值总和


    题解:

      DP很容易想到

      设f[i]为将前i个人分成若干段的最大真正战力值,s[i]为前i个人战力值总和

      可以得到:f[i]=max(f[j]+(s[i]-s[j])*(s[i]-s[j])*a+(s[i]-s[j])*b+c)

      但这道题数据极大

      所以用斜率优化DP

      设j1<j2<i

      f[j2]+(s[i]-s[j2])*(s[i]-s[j2])*a+(s[i]-s[j2])*b+c>f[j1]+(s[i]-s[j1])*(s[i]-s[j1])*a+(s[i]-s[j1])*b+c

      化简得到:(f[j2]-f[j1]+(s[j2]*s[j2]-s[j1]*s[j1])*a-(s[j2]-s[j1])*b)/(s[j2]-s[j1])>2*a*s[i]

      然后做斜率优化就可以了

      注意要加long long

      来自蒟蒻的吐槽:这道题!!我其实应该在n久之前就应该A了,结果在斜率优化的时候把slop(list[head],list[head+1])错手打成了slop(list[head],list[head]+1),痛心疾首


    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    LL a,b,c;
    LL s[1100000];
    LL f[1100000];
    /*
    f[i]=max(f[j]+(s[i]-s[j])*(s[i]-s[j])*a+(s[i]-s[j])*b+c)
    f[i]=max(f[j]+(s[i]-s[j])*(s[i]-s[j])*a+(s[i]-s[j])*b)+c
     
    j1<j2<i
    f[j2]+(s[i]-s[j2])*(s[i]-s[j2])*a+(s[i]-s[j2])*b > f[j1]+(s[i]-s[j1])*(s[i]-s[j1])*a+(s[i]-s[j1])*b
    f[j2]+s[i]*s[i]*a-2*a*s[i]*s[j2]+s[j2]*s[j2]*a+s[i]*b-s[j2]*b > f[j1]+s[i]*s[i]*a-2*a*s[i]*s[j1]+s[j1]*s[j1]*a+s[i]*b-s[j1]*b
    f[j2]-2*a*s[i]*s[j2]+s[j2]*s[j2]*a-s[j2]*b > f[j1]-2*a*s[i]*s[j1]+s[j1]*s[j1]*a-s[j1]*b
    (f[j2]-f[j1]+(s[j2]*s[j2]-s[j1]*s[j1])*a-(s[j2]-s[j1])*b)/(s[j2]-s[j1]) >2*a*s[i]
    */
    LL slop(int j1,int j2)
    {
        return (f[j2]-f[j1]+(s[j2]*s[j2]-s[j1]*s[j1])*a-(s[j2]-s[j1])*b)/(s[j2]-s[j1]);
    }
    int list[1100000];
    int main()
    {
        int n;
        scanf("%d",&n);
        scanf("%lld%lld%lld",&a,&b,&c);
        s[0]=0LL;
        for(int i=1;i<=n;i++)
        {
            LL x;
            scanf("%lld",&x);
            s[i]=s[i-1]+x;
        }
        int head=1,tail=1;list[1]=0;
        for(int i=1;i<=n;i++)
        {
            while(head<tail&&slop(list[head],list[head+1])>2LL*a*s[i]) head++;
            int j=list[head];
            f[i]=f[j]+(s[i]-s[j])*(s[i]-s[j])*a+(s[i]-s[j])*b+c;
            while(head<tail&&slop(list[tail-1],list[tail])<slop(list[tail],i)) tail--;
            list[++tail]=i;
        }
        printf("%lld
    ",f[n]);
        return 0;
    }

     

  • 相关阅读:
    【Linux基础总结】Linux基本环境
    mysql 源码安装
    windows内存映射文件
    TCHAR和CHAR类型的互转
    删除链表中重复的结点
    iptables防火墙
    两个链表的第一个公共结点
    无人值守安装linux系统
    dns服务 很多问题,后续再研究
    string 类型转换
  • 原文地址:https://www.cnblogs.com/Never-mind/p/8157186.html
Copyright © 2020-2023  润新知