P8726 [蓝桥杯 2020 省 AB3] 旅行家 题解
Super_Cube · · 题解
Solution
设
可以得到:
最后答案即为:
直接做复杂度过不去,考虑斜率优化,先将
以下内容是之前眼瞎的时候没看见数据范围里面保证了
现在的问题是式子中的斜率不单调的情况下连自变量也不单调,就不能像 P5785 那样用单调栈 + 二分进行维护凸包。
于是我们可以用 cdq 分治进行自变量的强制单调,这个时候你惊奇地发现斜率也随之单调了,所以在 cdq 内部处理时可以直接用单调队列维护凸包。
时间复杂度:
当然你可以在改写原 dp 式时像下面这样:
然后把它看成坐标系中的一次函数 / 一条直线,这题就变成李超线段树维护斜优 dp 了。
时间复杂度:
这个做法代码比 cdq 短,跑得也比 cdq 快。
下面是本题保证了
我们对于上式进行换元:
这样就可以很清晰的看出上式是类似于一次函数的。
我们为了让
所以我们要对于所有的决策点维护一个上凸包,这样我们拿斜率为
因为给定的
时间复杂度:
下面是这种方法的代码。
Code
#include<bits/stdc++.h>
#define X(i) (a[i])
#define Y(i) (dp[i]/2-b[i])
int dp[500005];
int a[500005],b[500005];
inline double slope(int l,int r){
if(X(l)==X(r))return (Y(r)-Y(l))*1e9;
return (Y(r)-Y(l))*1.0/(X(r)-X(l));
}
std::deque<int>q;
int n,ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;scanf("%d",&a[i++]));
for(int i=1;i<=n;scanf("%d",&b[i++]));
q.push_back(1);//从 1 号岛开始
for(int i=2;i<=n;++i){
while(q.size()>1&&slope(q[0],q[1])>=-a[i])q.pop_front();//把斜率比当前切线大的直接删掉
dp[i]=dp[q[0]]/2+a[i]*a[q[0]]-b[q[0]];//最优决策点转移
while(q.size()>1&&slope(*(q.end()-2),q.back())<=slope(q.back(),i))q.pop_back();//维护斜率单减
q.push_back(i);
ans=std::max(ans,dp[i]);
}
printf("%d",ans);
return 0;
}
/*
dp[i]=dp[j]/2+a[i]*a[j]-b[j]
dp[j]/2-b[j]=-a[i]*a[j]+dp[i]
*/