• #线段树分治,凸壳#洛谷 5416 [CTSC2016]时空旅行


    题目链接

    题目大意

    (n) 个平行宇宙,由某些平行宇宙添加或删除一个点(三维坐标)得到,

    现在有 (m) 组询问,形如对于某个平行宇宙,费用为从该平行宇宙内某个点出发

    到达指定 (x) 坐标的任意位置的欧几里得距离,

    并且这些点本身出发需要费用,如何让费用最小,

    保证原点在所有平行宇宙中均存在

    解题过程

    首先 (x) 坐标指定那么其实 (y) 坐标和 (z) 坐标可以被忽略,

    那么询问可以转化为形如找到一个存在的 (x)

    使得 (y=(x-x_0)^2+d)最小,其中(d)表示从这个 (x) 出发本身所需费用

    把这个平方拆开可以得到 (y=x^2-2xcdot x_0 + {x_0}^2+d)

    将常数项放前面可以得到 ({x_0}^2=2x_0cdot x-x^2+y-d)

    也就是各点坐标为((2x,-x^2+d)),用斜率为 (x_0) 的直线扫描使得截距最小,那么这也就是求一个下凸壳

    然后考虑平行宇宙怎么做,它会形成一棵树,那么点的存在就会锁定在一些dfs序的连续区间中,

    也就可以转化成线段树维护dfs序的区间,点按横坐标为第一关键字,纵坐标为第二关键字排序,

    如果强制在线询问的话那么就二分答案需要多带一个(log)

    否则可以按照斜率排序那么直接删除队头即可,时间复杂度(O(mlog_2n))

    代码

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #include <vector>
    #define rr register
    using namespace std;
    const int N = 500011;
    typedef long long lll;
    struct Point {
        lll x, y;
        inline Point operator -(const Point &t)const {
            return (Point) {
                x - t.x, y - t.y
            };
        }
    } a[N];
    struct rec {
        int x, z, rk;
    } q[N];
    struct node {
        int y, next;
    } e[N];
    vector<int>L[N], R[N];
    vector<Point>K[N << 2];
    lll ans[N];
    int n, m, Q, rk[N], head[N << 2], dfn[N], et, v[N], tot, Z[N], as[N];
    inline lll input() {
        rr lll ans = 0, f = 1;
        rr char c = getchar();
    
        while (!isdigit(c))
            f = (c == '-') ? -f : f, c = getchar();
    
        while (isdigit(c))
            ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
    
        return ans * f;
    }
    inline void print(lll ans) {
        if (ans < 0)
            putchar('-'), ans = -ans;
    
        if (ans > 9)
            print(ans / 10);
    
        putchar(ans % 10 + 48);
    }
    inline lll dj(Point x, Point y) {//数量积
        return x.x * y.x + x.y * y.y;
    }
    inline lll cj(Point x, Point y) {//向量积
        return x.x * y.y - x.y * y.x;
    }
    inline void add(int x, int y) {
        e[++et] = (node) {y, as[x]}, as[x] = et;
    }
    inline void update(int k, int l, int r, int x, int y, int z) {
        if (l == x && r == y) {
            while (K[k].size() > 1 && cj(a[z] - K[k][K[k].size() - 1], a[z] - K[k][K[k].size() - 2]) <= 0)
                K[k].pop_back();//下凸壳,也就是向量积为顺时针时弹出队尾
    
            K[k].push_back(a[z]);
            return;
        }
    
        rr int mid = (l + r) >> 1;
    
        if (y <= mid)
            update(k << 1, l, mid, x, y, z);
        else if (x > mid)
            update(k << 1 | 1, mid + 1, r, x, y, z);
        else
            update(k << 1, l, mid, x, mid, z), update(k << 1 | 1, mid + 1, r, mid + 1, y, z);
    }
    inline lll kxb(lll k, Point x) {//实际答案
        return k * x.x + x.y;
    }
    inline lll query(int k, int l, int r, int x, int z) {
        rr int len = K[k].size();
        rr lll ans = -2e18;
    
        if (len) {
            while (head[k] < len - 1 && kxb(z, K[k][head[k]]) <= kxb(z, K[k][head[k] + 1]))
                ++head[k];
    
            ans = kxb(z, K[k][head[k]]);
        }
    
        if (l == r)
            return ans;
    
        rr int mid = (l + r) >> 1;
    
        if (x <= mid)
            return max(ans, query(k << 1, l, mid, x, z));
        else
            return max(ans, query(k << 1 | 1, mid + 1, r, x, z));
    }
    inline void dfs(int x) {
        dfn[x] = ++tot;
    
        if (Z[x] > 0)
            L[Z[x]].push_back(tot);//插入操作的开头
    
        if (Z[x] < 0)
            R[-Z[x]].push_back(tot - 1);//删除操作的结尾
    
        for (rr int i = as[x]; i; i = e[i].next)
            dfs(e[i].y);
    
        if (Z[x] > 0)
            R[Z[x]].push_back(tot);//插入操作的结尾
    
        if (Z[x] < 0)
            L[-Z[x]].push_back(tot + 1);//删除操作的开头
    }
    bool cmp1(int x, int y) {//横坐标第一关键字
        return a[x].x < a[y].x || (a[x].x == a[y].x && a[x].y < a[y].y);
    }
    bool cmp2(rec x, rec y) {//按斜率排序
        return x.z < y.z;
    }
    signed main() {
        freopen("travel.in", "r", stdin);
        freopen("travel.out", "w", stdout);
        n = input(), Q = input(),
        L[1].push_back(1),
        R[1].push_back(n), v[1] = 1;
        a[m = 1] = (Point) {
            0, -input()
        };
    //令第一个点为原点
        for (rr int i = 2; i <= n; ++i) {
            rr int opt = iut();
            add(input() + 1, i);
            rr int z = input() + 2;
    
            if (!v[z])
                v[z] = ++m;//重新编号
    
            if (opt == 1)
                Z[i] = -v[z];//删除操作
            else {
                rr lll d = iut();
                input(), input();
                a[Z[i] = v[z]] = (Point) {
                    2 * d, -d *d - input()
                };//添加点
            }
        }
    
        for (rr int i = 1; i <= m; ++i)
            rk[i] = i;
    
        dfs(1), sort(rk + 1, rk + 1 + m, cmp1);
    
        for (rr int i = 1; i <= m; ++i) {
            rr int len = L[rk[i]].size();
    
            for (rr int j = 0; j < len; ++j) {
                rr int Ll = L[rk[i]][j], Rr = R[rk[i]][j];
    
                if (Ll <= Rr)
                    update(1, 1, n, Ll, Rr, rk[i]);
            }
        }
    
        for (rr int i = 1; i <= Q; ++i)
            q[i] = (rec) {
            dfn[input() + 1], input(), i
        };
    
        sort(q + 1, q + 1 + Q, cmp2);
    
        for (rr int i = 1; i <= Q; ++i)
            ans[q[i].rk] = 1ll * q[i].z * q[i].z - query(1, 1, n, q[i].x, q[i].z);
    
        for (rr int i = 1; i <= Q; ++i)
            print(ans[i]), putchar(10);
    
        return 0;
    }
    
  • 相关阅读:
    数据绑定表达式语法(Eval,Bind区别)
    使用博客园的第一件事 自定义主题
    sql2000 跨服务器复制表数据
    使用UpdatePanel 局部刷新出现中文乱码的解决方法!!
    MMC不能打开文件MSC文件
    sql 日期 、时间相关
    loaded AS2 swf call function in AS3 holder
    Rewrite the master page form action attribute in asp.net 2.0
    100万个不重复的8位的随机数
    flash 中实现斜切变型
  • 原文地址:https://www.cnblogs.com/Spare-No-Effort/p/14738127.html
Copyright © 2020-2023  润新知