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;
}