链接:https://www.nowcoder.com/acm/contest/94/G
来源:牛客网
题目描述
Blastix Riotz歌います大家都知道优酱的音游水平非常差但又特别喜欢装逼。有一天优酱走进机铺发现四周都是暴龙天(Music Voltex的目前最高段位)在愉快地打着超难高速交互曲目Blastix Riotz(别名“暴徒”),作为一名岳翔(Music Voltex的最低段位),优酱当然被吓得心肺停止,但是爱装逼的优酱还是故作镇定。她装作冷静地进行了一波达成率99%的分析,然后想到了一个用来计算打暴徒的最省体力的方案的数学模型,只要能解出这个模型的最优解,即使是岳翔也能轻松打通暴徒,瞬间变身暴龙天。你能帮助优酱实现这个宏伟的装逼梦想吗?
ガンバロウ!!ヴィヴィヴィオケー!(ドゥルル)ファファ!シマムラ!シミケン!エエェェ↓↓ エエェェェェ↓↓↓ウェェ↓キュウウゥッ↑サクセスデュゥゥ...あああああああああああああああああああああああああああああああああ
(亲爱的,你筐体塌了.jpg)
优酱的模型大概思路就是我们把整个谱面看成一个数组,数组上的每一个值表示把当前这个note打到Critical判定所需要的体力,而我们又知道,岳翔和暴龙天的游戏体验是显然不同的。如果我们把整个谱面拆分成一节一节的分段,对于岳翔来说,每一分段中的每一个note都要耗费体力,而暴龙天因为高超的技术可以让自己的体力耗费等于分段中最后一个note所需要的体力。所以如果我们可以最小化我们和暴龙天在完成整个谱面上的体力差距,我们就可以逆袭成功了。但是要注意,因为某些不可名状的原因,有时候经过划分后在某些段优酱的体力耗费甚至会低于暴龙天,在这种情况下因为“菜是原罪”的自然法则,这个时候优酱和暴龙天的体力耗费会被倒转。所以为了简化计算我们直接取二者差的平方,也就是MSE。我们将这一模型形式化如下:
我们给出两个由整数组成的,长度都为 的权值数组 和 。优酱要把数组的 下标划分为若干段 ,相邻的两段首尾相接,划分后每段 下标对应的的代价为 。我们的目标是帮优酱找到一种划分,使得所有段的代价总和最小。
解题思路:很容易写出状态转移方程 dp[i]=min(dp[i],dp[j-1]+cost(j,i))。对于cost我们可以预处理出前缀和,这样复杂度是O(n^2),明显不行。考虑优化,根据方程很容易看出来是斜率优化,顺势学了一波!!很有用,方程与博客类似,推出来就好了!
推荐博客:https://blog.csdn.net/lxc779760807/article/details/51366552
由于这里的斜率没有单调性,所以要用二分!
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+100;
typedef long long ll;
ll dp[MAXN];
ll b[MAXN];
ll a[MAXN];
ll sum[MAXN];
int que[MAX];//记录点
double K[MAX];//记录斜率
int head;
ll Y(int x,int y){return dp[x]+sum[x]*sum[x]-(dp[y]+sum[y]*sum[y]);}//推出来的
ll X(int x,int y){return (sum[x]-sum[y]);}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=n;i++)
scanf("%lld",&b[i]);
head=0;
for(int i=1;i<=n;i++)
{
int j=lower_bound(K+1,K+head+1,2*(sum[i]-b[i])*1.0)-K-1;//二分查找
j=que[j];
dp[i]=dp[j]+(sum[i]-b[i]-sum[j])*(sum[i]-b[i]-sum[j]);
//注意,防除0
while(head>0&&Y(i,que[head])*X(i,que[head-1])<=Y(i,que[head-1])*X(i,que[head]))head--;
if(X(i,que[head])==0)
K[head+1]=0x3f3f3f3f;
else
K[head+1]=1.0*Y(i,que[head])/X(i,que[head]);
que[head+1]=i;
head++;
}
printf("%lld\n",dp[n]);
}
return 0;
}