BZOJ[1713][Usaco2007 China]The Bovine Accordion and Banjo Orchestra 音乐会 二维斜率优化

传送门ber~

首先 i i 越大j越大
所以dp, fi,j f i , j 表示 ai a i bj b j 配的最大获利
所以有 fi,j=max{ft,k(sbj1sbk)2(sai1sat)2} f i , j = m a x { f t , k − ( s b j − 1 − s b k ) 2 − ( s a i − 1 − s a t ) 2 } ,其中 sasb s a , s b 两数组的前缀和
又可以发现最优的转移满足 t=i1k=j1 t = i − 1 , k = j − 1 这两个其中之一,因为如果不满足,可以选 ai1 a i − 1 bj1 b j − 1 配对,答案更优
所以dp方程式可以表示为 fi,j=max{fi1,t(sbj1sbt)2,fk,j1(sai1sak)2} f i , j = m a x { f i − 1 , t − ( s b j − 1 − s b t ) 2 , f k , j − 1 − ( s a i − 1 − s a k ) 2 }
现在两种转移分开考虑,化简,得到 t t k优的情况,满足

gtgksbtsbk>2sbj1 g t − g k s b t − s b k > − 2 s b j − 1 ①
gtgksatsak>2sai1 g t − g k s a t − s a k > − 2 s a i − 1 ②

其中①中的 gt=fi1,tsb2t g t = f i − 1 , t − s b t 2 ,②中的 gt=ft,j1sa2t g t = f t , j − 1 − s a t 2
维护两个上凸壳,搞一个二维的斜率优化就可以了

代码略(特别)丑
代码如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define INF 21474836470000ll
#define N 1050
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
typedef long long LL;
int n,l,r;
int a[N],b[N],ly[N],ry[N];
LL ans;
LL sa[N],sb[N],f[N][N];
struct Point{
    LL x,y;
    Point(){}
    Point(LL _,LL __):x(_),y(__){}
}qi[N],qj[N][N],tmp;
inline double Slope(Point a,Point b){
    if(a.x==b.x) return a.y>b.y?-INF:INF;
    return 1.0*(b.y-a.y)/(b.x-a.x);
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        sa[i]=sa[i-1]+a[i];
    }
    for(int i=1;i<=n;i++){
        b[i]=read();
        sb[i]=sb[i-1]+b[i];
    }
    for(int i=1;i<=n;i++)
        ly[i]=1;
    for(int i=1;i<=n;i++){
        l=1;r=0;
        for(int j=1;j<=n;j++){
            f[i][j]=a[i]*b[j]-sa[i-1]*sa[i-1]-sb[j-1]*sb[j-1];

            while(l<r && Slope(qi[l],qi[l+1])>-sb[j-1]*2)
                l++;
            if(i>0 && l<=r)
                f[i][j]=max(f[i][j],qi[l].y+sb[j-1]*2*qi[l].x-sb[j-1]*sb[j-1]+1ll*a[i]*b[j]);
            /上面是从i-1转移
            while(ly[j]<ry[j] && Slope(qj[j][ly[j]],qj[j][ly[j]+1])>-sa[i-1]*2)
                ly[j]++;
            if(j>0 && ly[j]<=ry[j])
                f[i][j]=max(f[i][j],qj[j][ly[j]].y+sa[i-1]*2*qj[j][ly[j]].x-sa[i-1]*sa[i-1]+1ll*a[i]*b[j]);
            /从j-1转移
            if(i-1){
                tmp=Point(sb[j],f[i-1][j]-sb[j]*sb[j]);
                while(l<r && Slope(qi[r],tmp)>Slope(qi[r-1],qi[r]))
                    r--;
                if(i-1) qi[++r]=tmp;
            }
            ///上下全是更新队列
            if(j-1){
                tmp=Point(sa[i],f[i][j-1]-sa[i]*sa[i]);
                while(ly[j]<ry[j] && Slope(qj[j][ry[j]],tmp)>Slope(qj[j][ry[j]-1],qj[j][ry[j]]))
                    ry[j]--;
                qj[j][++ry[j]]=tmp;
            }
        }
    }
    for(int i=1;i<=n;i++)
        ans=max(ans,max(f[i][n]-(sa[n]-sa[i])*(sa[n]-sa[i]),f[n][i]-(sb[n]-sb[i])*(sb[n]-sb[i])));
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值