最后一个测试点是什么毒瘤数据?
查看原帖
最后一个测试点是什么毒瘤数据?
602372
Brilliant11001楼主2024/7/17 15:59

rt,最后一个点死活过不去,都拍了 10000+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;
}
2024/7/17 15:59
加载中...