“景驰科技杯”2018年华南理工大学程序设计竞赛-G:Youhane as "Bang Riot"(斜率优化DP)

链接:https://www.nowcoder.com/acm/contest/94/G

时间限制:C/C++ 5秒,其他语言10秒
空间限制:C/C++ 116736K,其他语言233472K
64bit IO Format: %lld

题目描述

Blastix Riotz歌います
ガンバロウ!!ヴィヴィヴィオケー!(ドゥルル)ファファ!シマムラ!シミケン!エエェェ↓↓ エエェェェェ↓↓↓ウェェ↓キュウウゥッ↑サクセスデュゥゥ...あああああああああああああああああああああああああああああああああ
(亲爱的,你筐体塌了.jpg)




大家都知道优酱的音游水平非常差但又特别喜欢装逼。有一天优酱走进机铺发现四周都是暴龙天(Music Voltex的目前最高段位)在愉快地打着超难高速交互曲目Blastix Riotz(别名“暴徒”),作为一名岳翔(Music Voltex的最低段位),优酱当然被吓得心肺停止,但是爱装逼的优酱还是故作镇定。她装作冷静地进行了一波达成率99%的分析,然后想到了一个用来计算打暴徒的最省体力的方案的数学模型,只要能解出这个模型的最优解,即使是岳翔也能轻松打通暴徒,瞬间变身暴龙天。你能帮助优酱实现这个宏伟的装逼梦想吗?

优酱的模型大概思路就是我们把整个谱面看成一个数组,数组上的每一个值表示把当前这个note打到Critical判定所需要的体力,而我们又知道,岳翔和暴龙天的游戏体验是显然不同的。如果我们把整个谱面拆分成一节一节的分段,对于岳翔来说,每一分段中的每一个note都要耗费体力,而暴龙天因为高超的技术可以让自己的体力耗费等于分段中最后一个note所需要的体力。所以如果我们可以最小化我们和暴龙天在完成整个谱面上的体力差距,我们就可以逆袭成功了。但是要注意,因为某些不可名状的原因,有时候经过划分后在某些段优酱的体力耗费甚至会低于暴龙天,在这种情况下因为“菜是原罪”的自然法则,这个时候优酱和暴龙天的体力耗费会被倒转。所以为了简化计算我们直接取二者差的平方,也就是MSE。我们将这一模型形式化如下:

我们给出两个由整数组成的,长度都为 的权值数组 。优酱要把数组的 下标划分为若干段 ,相邻的两段首尾相接,划分后每段 下标对应的的代价为 。我们的目标是帮优酱找到一种划分,使得所有段的代价总和最小。

输入描述:

单文件,多数据,请循环读取至文件末尾。
对于每组数据,第一行为一个整数n,含义和范围请参见题目描述。
接下来的一行,为个用空格分割的整数Ai,和范围请参见题目描述。
接下来的一行,为个用空格分割的整数Bi,和范围请参见题目描述。

输出描述:

对于每组数据,输出一个整数,表示答案,独占一行。

示例1

输入

3
0 1 3
10 4 5
3
0 1 3
10 2 6

输出

1
4

思路:d[n]=min(d[j]+(sum[n]-b[n]-sum[j])*(sum[n]-b[n]-sum[j]))。

转化后为:d[j]+sum[j]*sum[j]=2*(sum[n]-b[n])*sum[j]+d[n]-(sum[n]-b[n])*(sum[n]-b[n])。

          即:y=2*k*x+b。

其中y=d[j]+sum[j]*sum[j],k=sum[n]-b[n],x=2*sum[j],b=d[n]-(sum[n]-b[n])*(sum[n]-b[n]).

所以用单调栈优化即可。但k<=0,k>=0可能都会存在,所以转移的时候应该用二分找到斜率大于当前斜率的点,只需开一个记录斜率的数组就行。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e6+100;
const int MOD=998244353;
const double PI=acos(-1);
typedef long long ll;
ll d[MAX],b[MAX];
ll sum[MAX];
int q[MAX];
double K[MAX];//记录斜率
ll Y(int x,int y){return d[x]+sum[x]*sum[x]-(d[y]+sum[y]*sum[y]);}//Y[x]-Y[y];
ll X(int x,int y){return 2*(sum[x]-sum[y]);}                      //X[x]-X[y];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        sum[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&sum[i]);
            sum[i]+=sum[i-1];
        }
        for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
        int R=0;
        d[0]=0;
        q[0]=0;
        for(int i=1;i<=n;i++)
        {
            int index=lower_bound(K+1,K+R+1,(sum[i]-b[i])*1.0)-K-1;
            index=q[index];
            d[i]=d[index]+(sum[i]-b[i]-sum[index])*(sum[i]-b[i]-sum[index]);
            while(0<R&&Y(i,q[R])*X(i,q[R-1])<=Y(i,q[R-1])*X(i,q[R]))R--;
            if(X(i,q[R])==0)K[R+1]=1e18;
            else K[R+1]=1.0*Y(i,q[R])/X(i,q[R]);
            q[R+1]=i;
            R++;
        }
        printf("%lld\n",d[n]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值