LOJ #3159. 「NOI2019」弹跳

题目叙述

\(n\) 个平面上的点,每个点有一些机关可以跳到一个矩形范围内的其他点,花费时间 \(t_i\) ,求一号点到其他所有点的最短路。

题解

这就是一个优化建图题,但是是二维的,考虑 k-d tree 优化建图。
但是发现边数比较玄学,容易开不下,那么不连边了,直接在 k-d tree 上跑 dijkstra 算法。
有人说可以直接在 k-d tree 上维护最小值,在 k-d tree 上打取最小值的标记。
但是这样并不好,因为 k-d tree 部分是 \(n\sqrt{n}\) 的,常数不应该加在这上面。
而是应该搞一个堆,每次把更新的最小值塞到里面,因为每个节点只会被删除一次,给其他节点更新也不过就是边数次,所以堆其实并不会给复杂度瓶颈增加一个 \(\log\) ,而只会加上一个 \(m\log m\)
这题里的 dijkstra 需要判定一个子树内是否还剩下一个节点没有确定最短路是多少。

总结

  • 卡常:注意复杂度瓶颈的常数不要太大。
  • 可以不建图跑 dijkstra 。
  • 写dijkstra,如果有必要,可以在那个堆里直接存边松弛完了的结果。

代码

这是 EmptySoulist 的代码,我的代码是在 k-d tree 上维护的最小值,不太好。

#include <bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( int i = (t); i >= (s); -- i )
#define mp make_pair
#define pi pair<int, int>
#define pb push_back
#define vi vector<int>
int gi() {
    char cc = getchar() ;
    int cn = 0, flus = 1 ;

    while (cc < '0' || cc > '9') {
        if (cc == '-')
            flus = - flus ;

        cc = getchar() ;
    }

    while (cc >= '0' && cc <= '9')
        cn = cn * 10 + cc - '0', cc = getchar() ;

    return cn * flus ;
}
const int N = 7e4 + 5 ;
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
struct KDT {
    int xl, xr, yl, yr, w, id ;
} tr[N << 2] ;
int n, m, W, H, ans[N] ;
struct node {
    int x, y, id ;
} p[N] ;
bool cmp1(node x, node y) {
    return x.x < y.x ;
}
bool cmp2(node x, node y) {
    return x.y < y.y ;
}
void pushup(int x) {
    tr[x].xl = min(tr[ls(x)].xl, tr[rs(x)].xl),
         tr[x].xr = max(tr[ls(x)].xr, tr[rs(x)].xr),
              tr[x].yl = min(tr[ls(x)].yl, tr[rs(x)].yl),
                   tr[x].yr = max(tr[ls(x)].yr, tr[rs(x)].yr),
                        tr[x].w = tr[ls(x)].w + tr[rs(x)].w ;
}
void build(int x, int l, int r, int type) {
    if (l == r)
        return tr[x] = (KDT) {p[l].x, p[l].x, p[l].y, p[l].y, 1, p[l].id}, void() ;
    int mid = (l + r) >> 1 ;
    nth_element(p + l, p + mid, p + r + 1, (type) ? cmp1 : cmp2) ;
    build(ls(x), l, mid, !type), build(rs(x), mid + 1, r, !type),
          pushup(x) ;
}
bool check(KDT a, KDT b) { //a 是否被 b 包含
    if (a.xl > b.xr || a.xr < b.xl || a.yl > b.yr || a.yr < b.yl)
        return 0 ; //相离

    if (b.xl <= a.xl && b.xr >= a.xr && b.yl <= a.yl && b.yr >= a.yr)
        return 2 ;

    return 1 ;
}
struct JX {
    int t, l, r, d, u ;
    bool operator < (const JX a) const {
        return t > a.t ;
    }
} ;
vector<JX> E[N] ;
priority_queue<JX> q ;
void Ins(int x) {
    //  printf("now Ins %d\n", x ) ;
    for (JX e : E[x])
        q.push((JX) {
        ans[x] + e.t, e.l, e.r, e.d, e.u
    }) ;
}
void solve(int x, int l, int r, KDT c) {
    int d = check(tr[x], c) ;

    //  printf("[%d %d, %d %d] & [%d %d, %d %d]\n",
    //  tr[x].xl, tr[x].xr, tr[x].yl, tr[x].yr,
    //  c.xl, c.xr, c.yl, c.yr) ;
    //  printf("now solve %d [%d %d] %d\n", x, l, r, d ) ;
    if (!d || !tr[x].w)
        return ;

    if (l == r) {
        ans[tr[x].id] = c.w, tr[x].w = 0, Ins(tr[x].id) ;
        return ;
    }

    int mid = (l + r) >> 1 ;
    solve(ls(x), l, mid, c), solve(rs(x), mid + 1, r, c),
          tr[x].w = tr[ls(x)].w + tr[rs(x)].w ;
}
signed main() {
    freopen("jump.in", "r", stdin) ;
    freopen("jump.out", "w", stdout) ;
    n = gi(), m = gi(), W = gi(), H = gi() ;
    rep(i, 1, n) p[i].x = gi(), p[i].y = gi(), p[i].id = i ;
    int xx = p[1].x, yy = p[1].y ;
    rep(i, 1, m) {
        int id = gi(), t = gi(), l = gi(), r = gi(), d = gi(), u = gi() ;
        E[id].pb((JX) {
            t, l, r, d, u
        }) ;
    }
    build(1, 1, n, 0) ;
    solve(1, 1, n, (KDT) {
        xx, xx, yy, yy, 0, 0
    }) ;

    while (!q.empty()) {
        JX u = q.top() ;
        q.pop() ;
        //  printf("now solve %d [%d %d] [%d %d]\n", u.t, u.l, u.r, u.d, u.u) ;
        solve(1, 1, n, (KDT) {
            u.l, u.r, u.d, u.u, u.t, 0
        }) ;
    }

    rep(i, 2, n) printf("%d\n", ans[i]) ;
    return 0 ;
}

我的代码

#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 7e4 + 5, MAXEDGE = 15e4 + 5, dieinf = 0x3f3f3f3f, liveinf = 1e9;
int head[MAXN], edgenum, sht[MAXN];
struct E {
    int len, L, R, U, D, nxt;
    E(int _l = 0, int _L = 0, int _R = 0, int _D = 0, int _U = 0, int _n = 0)
        : len(_l), L(_L), R(_R), U(_U), D(_D), nxt(_n) {}
} edge[MAXEDGE];
void AddE(int u, int len, int L, int R, int D, int U) {
    edge[++edgenum] = E(len, L, R, D, U, head[u]);
    head[u] = edgenum;
}
struct Dot {
    int d[2];
    Dot(int x = 0, int y = 0) {
        d[0] = x;
        d[1] = y;
    }
};
struct cy {
    Dot ps;
    int id;
} city[MAXN];
int totkd, N, M, W, H, rt, D;
bool cmp(const cy &x, const cy &y) {
    return x.id < y.id;
}
bool operator<(const cy &x, const cy &y) {
    return x.ps.d[D] < y.ps.d[D];
}
struct kdnode {
    int mn[2], mx[2], ch[2], L, R, mndis, mnpos, slacknum;
    bool lazy;
} kd[MAXN * 2];
void Upd1(int u) {
    kd[u].mn[0] = min(kd[kd[u].ch[0]].mn[0], kd[kd[u].ch[1]].mn[0]);
    kd[u].mn[1] = min(kd[kd[u].ch[0]].mn[1], kd[kd[u].ch[1]].mn[1]);
    kd[u].mx[0] = max(kd[kd[u].ch[0]].mx[0], kd[kd[u].ch[1]].mx[0]);
    kd[u].mx[1] = max(kd[kd[u].ch[0]].mx[1], kd[kd[u].ch[1]].mx[1]);
}
int Diff(int L, int R, int wd) {
    int mn = 1e9, mx = 0;

    for (int i = L; i <= R; ++i) {
        mn = min(mn, city[i].ps.d[wd]);
        mx = max(mx, city[i].ps.d[wd]);
    }

    return mx - mn;
}
bool getwd(int L, int R) {
    return Diff(L, R, 0) < Diff(L, R, 1);
}
void Upd2(int u) { //很明显最小不能给 -1,所以要特判 -1
    if (kd[kd[u].ch[0]].mnpos != -1 && kd[kd[u].ch[1]].mnpos != -1) {
        if (kd[kd[u].ch[0]].mndis > kd[kd[u].ch[1]].mndis) {
            kd[u].mndis = kd[kd[u].ch[1]].mndis;
            kd[u].mnpos = kd[kd[u].ch[1]].mnpos;
        } else {
            kd[u].mndis = kd[kd[u].ch[0]].mndis;
            kd[u].mnpos = kd[kd[u].ch[0]].mnpos;
        }
    } else {
        if (kd[kd[u].ch[0]].mnpos == -1) { //到底还是得特判-1,偷懒分为 dieinf和 lievinf还会有错
            kd[u].mnpos = kd[kd[u].ch[1]].mnpos;
            kd[u].mndis = kd[kd[u].ch[1]].mndis;
        } else {
            kd[u].mnpos = kd[kd[u].ch[0]].mnpos;
            kd[u].mndis = kd[kd[u].ch[0]].mndis;
        }
    }
}
int Build(int L, int R) {
    int nw = ++totkd;
    kd[nw].L = L;
    kd[nw].R = R;

    if (L == R) {
        kd[nw].mn[0] = kd[nw].mx[0] = city[L].ps.d[0];
        kd[nw].mn[1] = kd[nw].mx[1] = city[L].ps.d[1];
        kd[nw].mndis = liveinf;
        kd[nw].mnpos = city[L].id;
        sht[city[L].id] = L;
        return nw;
    }

    D = getwd(L, R);
    int mid = (L + R) >> 1;
    nth_element(city + L, city + mid, city + R + 1);
    kd[nw].ch[0] = Build(L, mid);
    kd[nw].ch[1] = Build(mid + 1, R);
    Upd1(nw);
    Upd2(nw);
    return nw;
}
void Do(int now) {
    kd[now].mndis = min(kd[now].mndis, kd[now].slacknum);
}
void Spread(int now) {
    if (kd[now].lazy) {
        if (kd[kd[now].ch[0]].lazy)
            kd[kd[now].ch[0]].slacknum = min(kd[kd[now].ch[0]].slacknum, kd[now].slacknum);
        else {
            kd[kd[now].ch[0]].lazy = 1;
            kd[kd[now].ch[0]].slacknum = kd[now].slacknum;
        }

        Do(kd[now].ch[0]);

        if (kd[kd[now].ch[1]].lazy)
            kd[kd[now].ch[1]].slacknum = min(kd[kd[now].ch[1]].slacknum, kd[now].slacknum);
        else {
            kd[kd[now].ch[1]].lazy = 1;
            kd[kd[now].ch[1]].slacknum = kd[now].slacknum;
        }

        Do(kd[now].ch[1]);
        kd[now].lazy = 0;
    }
}
void SlackNode(int now, int nwdis, int L, int R, int D, int U) {
    if (kd[now].mn[0] > R || kd[now].mx[0] < L || kd[now].mn[1] > U || kd[now].mx[1] < D)
        return ;

    if (L <= kd[now].mn[0] && kd[now].mx[0] <= R && D <= kd[now].mn[1] && kd[now].mx[1] <= U) {
        if (!kd[now].lazy || kd[now].slacknum > nwdis) { //这里也应该判断是否有 lazy...
            kd[now].slacknum = nwdis;
            kd[now].lazy = 1;
            Do(now);
        }

        return ;
    }

    Spread(now);
    SlackNode(kd[now].ch[0], nwdis, L, R, D, U);
    SlackNode(kd[now].ch[1], nwdis, L, R, D, U);
    Upd2(now);
}
void ModifyExist(int now, int u) {
    if (kd[now].L == kd[now].R) {
        kd[now].mndis = dieinf;
        kd[now].mnpos = -1;
        return ;
    }

    Spread(now);
    int mid = (kd[now].L + kd[now].R) >> 1;

    if (sht[u] <= mid) //有可能有重复坐标,所以这样写不完全对
        ModifyExist(kd[now].ch[0], u);
    else
        ModifyExist(kd[now].ch[1], u);

    Upd2(now);
}
void ModifyDis(int now, int u, int d) {
    if (kd[now].L == kd[now].R) {
        kd[now].mndis = d;
        return ;
    }

    Spread(now);
    int mid = (kd[now].L + kd[now].R) >> 1;

    if (sht[u] <= mid) //这是 <=
        ModifyDis(kd[now].ch[0], u, d);
    else
        ModifyDis(kd[now].ch[1], u, d);

    Upd2(now);
}
int ans[MAXN];
void Dij() {
    sort(city + 1, city + N + 1, cmp);
    ModifyDis(rt, 1, 0);

    for (int i = 1; i <= N; ++i) { //需要进行 N次找到最小值并松弛
        int u = kd[rt].mnpos, disu = kd[rt].mndis;
        ans[u] = disu;
        ModifyExist(rt, u);

        for (int p = head[u]; p; p = edge[p].nxt)
            SlackNode(rt, disu + edge[p].len, edge[p].L, edge[p].R, edge[p].D, edge[p].U);
    }
}
int main() {
    freopen("jump.in", "r", stdin);
    freopen("jump.out", "w", stdout);
    scanf("%d%d%d%d", &N, &M, &W, &H);

    for (int i = 1; i <= N; ++i) {
        scanf("%d%d", &city[i].ps.d[0], &city[i].ps.d[1]);
        city[i].id = i;
    }

    for (int i = 1; i <= M; ++i) {
        int p, t, l, r, d, u;
        scanf("%d%d%d%d%d%d", &p, &t, &l, &r, &d, &u);
        AddE(p, t, l, r, d, u);
    }

    rt = Build(1, N);
    Dij();

    for (int i = 2; i <= N; ++i)
        printf("%d\n", ans[i]);

    fclose(stdin);
    fclose(stdout);
    return 0;
}
posted @ 2022-07-24 11:41  YouthRhythm  阅读(32)  评论(0编辑  收藏  举报