• P5504 [JSOI2011]柠檬


    传送门

    显然考虑 $dp$ ,发现从右往左和从左往右是一样的,所以只考虑一边就行

    发现对于切的左右端点,选择的 $s0$ 一定要为左右端点的贝壳大小,不然这个端点不产生贡献还不如分开来单个贡献

    所以设 $f[i]$ 表示当前把 $1$ 到 $i$ 的都切了,产生的最大贡献,设 $c[i]$ 表示位置 $i$ 及之前大小为 $s[i]$ 的柠檬个数,有转移:

    $f[i]=f[j-1]+s[i](c[i]-c[j]+1)^2,j in [1,i]$,并且要满足 $s[i]=s[j]$ ,发现是个斜率优化的式子,拆开来:

    $f[i]=f[j-1]+s[i](c[i]^2-2c[i](c[j]-1)+(c[j]-1)^2)$,再拆,变成

    $f[j-1]+s[i](c[j]-1)^2=2s[i]c[i](c[j]-1)+f[i]-s[i]c[i]^2$,因为转移都是在同一个大小之间转移,所以 $s[i]$ 可以看成常数

    所以 $y=f[j-1]+s[i](c[j]-1)^2$,$k=2s[i]c[i]$,$x=c[j]-1$,$b=f[i]-s[i]c[i]^2$,对每种 $s$ 都维护一个凸包即可

    显然对于同一个 $s$, $k,x$ 都单调递增,并且求 $max$ ,所以维护上凸包

    插点时从右边插,更新 $f$ 时也切凸包右边,用 $vector$ 维护凸包即可

    注意先加当前点再更新 $f$($j in [1,i]$)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    typedef long double ldb;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7,M=2e4+7;
    int n,s[N],c[N],cnt[M];
    ll f[N];
    struct Vec{//向量
        ldb x,y;
        Vec (ll a=0,ll b=0) { x=a,y=b; }
        inline ldb operator * (const Vec &tmp) const {
            return x*tmp.y-y*tmp.x;
        }
    };
    struct Poi{//凸包点
        ll f; int cj,s;
        Poi (ll a=0,int b=0,int c=0) { f=a,cj=b,s=c; }
        inline ll calc(int i) { return f+1ll*s*(c[i]-cj+1)*(c[i]-cj+1); }
        inline ll X() { return 1ll*s*(cj-1); }
        inline ll Y() { return f+1ll*s*(cj-1)*(cj-1); }
    };
    inline Vec operator - (Poi &A,Poi &B) {
        return Vec( A.X()-B.X() , A.Y()-B.Y() );
    }
    vector <Poi> st[M];//每种s维护凸包
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++)
            s[i]=read(),c[i]=++cnt[s[i]];
        for(int i=1;i<=n;i++)
        {
            Poi t(f[i-1],c[i],s[i]); int len=st[s[i]].size()-1;
            while( len>0 && (st[s[i]][len]-st[s[i]][len-1])*(t-st[s[i]][len-1])>=0 ) st[s[i]].pop_back(),len--;
            st[s[i]].push_back(t); len++;//先插入
            while( len>0 && st[s[i]][len].calc(i) <= st[s[i]][len-1].calc(i) ) st[s[i]].pop_back(),len--;
            f[i]=st[s[i]][len].calc(i);//再更新
        }
        printf("%lld
    ",f[n]);
        return 0;
    }
  • 相关阅读:
    static关键字的定义与使用
    String类练习统计一个字符串中大小写字母及数字字符个数
    Java中String类的常用方法
    String类的特点和使用步骤
    HTB 渗透测试笔记-Lame
    消息认证-数字签名-报文鉴别-到底是什么
    docker pull 太慢了解决办法
    彻底解决Mac无线网络故障和网速慢的问题
    彻底-有效-解决-Github下载太慢的问题
    Linux中的docker报错 Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11387738.html
Copyright © 2020-2023  润新知