rt,最后一个点死活过不去,都拍了 10000+ 组数据了还找不出错。
调了一上午,红温了。
求大佬提出宝贵的修改意见。
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 200010;
const double eps = 1e-10, inf = 1e18;
typedef long long ll;
int n;
ll h[N], sumw[N], dp[N];
struct node{
int s[2], p;
double xs, ys, lk, rk;
void init(int p_, double xs_, double ys_) {p = p_, xs = xs_, ys = ys_;}
}tr[N];
int root, idx;
inline double X(int x) {return h[x];} //横坐标
inline double Y(int x) {return dp[x] + h[x] * h[x] - sumw[x];} //纵坐标
inline double slope(int i, int j) {
if(fabs(tr[i].xs - tr[j].xs) < eps) return tr[i].ys < tr[j].ys ? inf : -inf; //特判直线垂直于 x 轴的情况
return (tr[i].ys - tr[j].ys) / (tr[i].xs - tr[j].xs);
}
void rotate(int x) {
int y = tr[x].p, z = tr[y].p;
int k = x == tr[y].s[1];
tr[z].s[y == tr[z].s[1]] = x, tr[x].p = z;
tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
tr[x].s[k ^ 1] = y, tr[y].p = x;
}
inline void splay(int x, int &k) {
if (x == k) return ;
for (int p = tr[k].p, y = tr[x].p; y != p; y = tr[x].p) {
if (tr[y].p != p) {
if((x == tr[y].s[1]) ^ (y == tr[tr[y].p].s[1])) rotate(x);
else rotate(y);
}
rotate(x);
}
k = x;
}
inline void insert(double xs, double ys) {
int u = root, p = 0;
while(u) p = u, u = tr[u].s[xs + eps > tr[u].xs];
u = ++idx;
if(p) tr[p].s[xs + eps > tr[p].xs] = u;
tr[u].init(p, xs, ys);
splay(u, root);
}
inline int get_prev(int x) {
int ans = 0;
for (int y = tr[x].s[0]; y;) {
if (tr[y].lk < slope(y, x) + eps) ans = y, y = tr[y].s[1];
else y = tr[y].s[0];
}
return ans;
}
inline int get_next(int x) {
int ans = 0;
for (int y = tr[x].s[1]; y;) {
if (slope(x, y) < tr[y].rk + eps) ans = y, y = tr[y].s[0];
else y = tr[y].s[1];
}
return ans;
}
//以上为 Splay 模板
//维护凸壳
inline void update(int &rt, int x) {
splay(x, rt);
if (tr[x].s[0]) {
int y = get_prev(x);
if(y) { //有可能有左子树但左子树上的点都不满足凸包性质,所以要特判
splay(y, tr[x].s[0]);
tr[y].s[1] = 0; // 清空那些不满足凸包性质的点
tr[y].rk = tr[x].lk = slope(y, x); //计算当前点的左右斜率
}
else tr[x].s[0] = 0, tr[x].lk = -inf; //左边没有点能满足凸包性质,全部删掉且斜率为负无穷
} else tr[x].lk = -inf;
if (tr[x].s[1]) { //同上
int y = get_next(x);
if(y) {
splay(y, tr[x].s[1]);
tr[y].s[0] = 0;
tr[x].rk = tr[y].lk = slope(x, y);
}
else tr[x].s[1] = 0, tr[x].rk = inf;
} else tr[x].rk = inf;
if (tr[x].lk + eps > tr[x].rk) { //判断当前点是否位于凸壳,若 if 成立则不在凸壳上,要删除
rt = tr[x].s[0];
tr[tr[rt].s[1] = tr[x].s[1]].p = rt;
tr[rt].p = 0;
tr[rt].rk = tr[tr[rt].s[1]].lk = slope(rt, tr[rt].s[1]);
}
}
//维护下凸包,寻找第一个斜率大于当前直线的点
//在 Splay 上二分
//若当前直线的斜率在当前点左右斜率之间,则找到
//若当前点的左斜率大于当前直线的斜率,说明要找的点在左侧,递归左子树
//否则在右侧,递归右子树
int get_strategy(int u, double k) {
if(!u) return 0;
if(tr[u].lk < k + eps && k < tr[u].rk + eps) return u;
if(tr[u].lk + eps >= k) return get_strategy(tr[u].s[0], k);
else return get_strategy(tr[u].s[1], k);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lld", &h[i]);
for(int i = 1; i <= n; i++) {
scanf("%lld", &sumw[i]);
sumw[i] += sumw[i - 1];
}
dp[1] = 0;
dp[2] = dp[1] + (h[2] - h[1]) * (h[2] - h[1]);
insert(X(1), Y(1));
update(root, 1);
insert(X(2), Y(2));
update(root, 2); //先插入两个基本的点
for(int i = 3; i <= n; i++) {
int j = get_strategy(root, 2.0 * h[i]); //求出决策点
dp[i] = dp[j] + (h[i] - h[j]) * (h[i] - h[j]) + sumw[i - 1] - sumw[j]; //更新
insert(X(i), Y(i));
update(root, i); //插入新决策
}
printf("%lld", dp[n]);
return 0;
}