LUOGU 5417: [NOI2019] KD tree Bounce

title

LUOGU 5471

analysis

\ (32pts \) : the structure for recording even at the edge of each range, \ (O (N ^ 2) \) violent even edge, and then run again \ (the Dijkstra \) can.

\ (100 pts \) : Consider \ (Dijkstra \) process: maintaining a set of points, each time out points set in to the nearest point source, use it to update other point to source point distance, and then delete this point.

Then we want to maintain equivalent (to the source from which point on the grid is) a two-dimensional chessboard, and supports the following operations:

  • Queries the global minimum point;
  • Update sub-matrix in the value of the squares of all;
  • Delete one grid;

Probably a \ (KD ~ tree \) it.

Time to update the sub-matrix just like to put the tree line mark, delete as scapegoat tree as lazy deletion.

Of course, there is a simple way, see siruiyang_sry .


Recall the summer of the following conditions of sync when playing game, really is a bit miserable, This question is indeed, a very good take minutes or violence (for now), I do not know what I'm busy, anyway, is really a thing, always, it's up and down with time, we will need to put together, and mastered.

code

#include <bits/stdc++.h>

#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)

#define Grt ch = getchar()
#define DeBug(x) std::cout << #x << " = " << x << std::endl

const int MaxN = 7e4 + 10, inf = 0x3f3f3f3f;

namespace IO
{
    char buf[1<<15], *fs, *ft;
    inline char getc() { return ft == fs && (ft = (fs = buf) + fread(buf, 1, 1<<15, stdin), ft == fs) ? 0 : *fs++; }
    template <typename T> inline void read(T &x)
    {
        x = 0;
        T f = 1, Grt;
        while (!isdigit(ch) && ch ^ '-') Grt;
        if (ch == '-') f = -1, Grt;
        while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), Grt;
        x *= f;
    }

    template <typename T, typename... Args>
    inline void read(T &x, Args &...args) { read(x); read(args...); }

    char Out[1<<24], *fe = Out;
    inline void flush() { fwrite(Out, 1, fe - Out, stdout); fe = Out; }
    template <typename T> inline void write(T x, char str)
    {
        if (!x) *fe++ = 48;
        if (x < 0) *fe++ = '-', x = -x;
        T num = 0, ch[20];
        while (x) ch[++ num] = x % 10 + 48, x /= 10;
        while (num) *fe++ = ch[num --];
        *fe++ = str;
    }
}

using IO::read;
using IO::write;

template <typename T> inline bool chkMin(T &a, const T &b) { return a > b ? (a = b, true) : false; }
template <typename T> inline bool chkMax(T &a, const T &b) { return a < b ? (a = b, true) : false; }
template <typename T> inline T min(T a, T b) { return a < b ? a : b; }
template <typename T> inline T max(T a, T b) { return a > b ? a : b; }

struct point{int x, y;} o[MaxN];//存储每一个点
int cmptype, rank[MaxN];
inline bool cmp(int i, int j)
{
    if (!cmptype) return o[i].x < o[j].x;
    else return o[i].y < o[j].y;
}

namespace SGT
{
    struct Orz{point p, lp, rp; int l, r, Max, Min, val, id, tag, vis;} c[MaxN];
    int cnt = 0;
    inline void pushup(int x)//维护节点所代表的的矩阵
    {
        if (c[x].vis) c[x].lp = (point){inf, inf}, c[x].rp = (point){-inf, -inf};
        else c[x].lp = c[x].rp = c[x].p;
        int l = c[x].l, r = c[x].r;
        if (l)
        {
            chkMin(c[x].lp.x, c[l].lp.x);
            chkMin(c[x].lp.y, c[l].lp.y);
            chkMax(c[x].rp.x, c[l].rp.x);
            chkMax(c[x].rp.y, c[l].rp.y);
        }
        if (r)
        {
            chkMin(c[x].lp.x, c[r].lp.x);
            chkMin(c[x].lp.y, c[r].lp.y);
            chkMax(c[x].rp.x, c[r].rp.x);
            chkMax(c[x].rp.y, c[r].rp.y);
        }
    }

    inline void update(int x)//维护子树的最大最小值(最大值可用于剪枝)
    {
        if (c[x].vis) c[x].Min = inf, c[x].Max = -inf;
        else c[x].Min = c[x].Max = c[x].val;
        int l = c[x].l, r = c[x].r;
        if (l)
        {
            chkMax(c[x].Max, c[l].Max);
            chkMin(c[x].Min, c[l].Min);
        }
        if (r)
        {
            chkMax(c[x].Max, c[r].Max);
            chkMin(c[x].Min, c[r].Min);
        }
    }

    inline int build(int l, int r, int type)
    {
        if (l > r) return 0;
        cmptype = type;//记录此时的排序方式,因为是二维轮换建树
        int mid = (l + r) >> 1, x = ++ cnt;
        std::nth_element(rank + l, rank + mid, rank + r + 1, cmp);//令 rank[mid] 放在第 mid 位上,但不保证其他元素有序
        c[x].p = o[rank[mid]], c[x].id = rank[mid], c[x].val = c[x].tag = inf, c[x].vis = 0;
        if (c[x].id == 1) c[x].val = 0;//初始化这个节点
        c[x].l = build(l, mid - 1, type ^ 1);//构造左右子树
        c[x].r = build(mid + 1, r, type ^ 1);
        pushup(x), update(x);
        return x;
    }

    inline void addTag(int x, int v)//放标记:由于这个矩形内所有点到源点的距离会变得最大是 v ,所以最大值需要更新
    {
        if (v < c[x].Max && v < c[x].tag)
        {
            c[x].Max = c[x].tag = v, chkMin(c[x].Min, v);
            if (!c[x].vis) chkMin(c[x].val, v);
        }
    }

    inline void pushdown(int x)//下方标记,很类似于 Splay
    {
        if (c[x].tag == inf) return ;
        if (c[x].l) addTag(c[x].l, c[x].tag);
        if (c[x].r) addTag(c[x].r, c[x].tag);
        c[x].tag = inf;
    }

    inline int find(int x, int v)//找到离源点距离为 v 的点
    {
        pushdown(x);
        if (!c[x].vis && v == c[x].val)//除的时候注意要保留节点的 val ,因为统计答案的时候有用
        {
            c[x].vis = 1;
            pushup(x), update(x);
            return x;
        }
        int l = c[x].l, r = c[x].r;
        if (l && v == c[l].Min)//向左子树寻找
        {
            int ans = find(l, v);
            pushup(x), update(x);
            return ans;
        }
        else//反之向右子树寻找
        {
            int ans = find(r, v);
            pushup(x), update(x);
            return ans;
        }
    }

    inline void Change(int x, point tl, point tr, int v)//更新子矩阵
    {
        pushdown(x);
        if (v >= c[x].Max) return ;//剪枝
        if (tl.x > c[x].rp.x || tr.x < c[x].lp.x || tl.y > c[x].rp.y || tr.y < c[x].lp.y) return ;
        if (tl.x <= c[x].lp.x && c[x].rp.x <= tr.x && tl.y <= c[x].lp.y && c[x].rp.y <= tr.y)
        {
            addTag(x, v);
            return ;
        }
        if (!c[x].vis && tl.x <= c[x].p.x && c[x].p.x <= tr.x && tl.y <= c[x].p.y && c[x].p.y <= tr.y) chkMin(c[x].val, v);
        if (c[x].l) Change(c[x].l, tl, tr, v);
        if (c[x].r) Change(c[x].r, tl, tr, v);
        update(x), pushup(x);
    }
}

using SGT::build;
using SGT::find;
using SGT::Change;
using SGT::c;

struct edge{point lp, rp; int len;};
std::vector<edge> E[MaxN];
int ans[MaxN];
int main()
{
    int n, m, W, H; read(n, m, W, H);
    for (int i = 1; i <= n; ++ i) read(o[i].x, o[i].y), rank[i] = i;
    for (int i = 1, p, t, l, r, u, d; i <= m; ++ i)
    {
        read(p, t, l, r, u, d);
        E[p].push_back((edge){(point){l, u}, (point){r, d}, t});//记录边,要素:左上点,右下点,代价
    }
    int root = build(1, n, 0);//二维轮换建树
    for (int i = 1; i <= n; ++ i)
    {
        int pos = find(root, c[root].Min);
        int ip = c[pos].id;
        ans[ip] = c[pos].val;//这里的 code 就很清晰了,不多解释
        for (int j = 0; j < (int)E[ip].size(); ++ j) Change(root, E[ip][j].lp, E[ip][j].rp, ans[ip] + E[ip][j].len);
    }//更新这个点所能到达的矩阵
    for (int i = 2; i <= n; ++ i) write(ans[i], '\n');
    IO::flush();
    return 0;
}

Guess you like

Origin www.cnblogs.com/G-hsm/p/Noi2019D2T1.html