Description

平面上有 \(n\) 个点,分布在 \(w \times h\) 的网格上。有 \(m\) 个弹跳装置,由一个六元组描述。第 \(i\) 个装置有参数:\((p_i, t_i, L_i, R_i, D_i, U_i)\),表示它属于 \(p_i\) 号点,从点 \(p_i\) 可以通过这个装置跳到任意一个满足 \(x\) 坐标 \(\in[L_i, R_i]\),\(y\) 坐标 \(\in [D_i, U_i]\) 的点,耗时 \(t_i\)。

现给出点 \(i\sim n\) 的坐标,\(m\) 开个弹跳装置的信息。对所有的 \(i\in(1, n]\),求 \(1\) 号点到 \(i\) 号点的最短耗时。

Hint

  • \(1\le n\le 7\times 10^4\)
  • \(1\le m\le 1.5\times 10^5\)
  • \(1\le w, h\le n\)
  • \(1\le t_i\le 10^4\)

Solution

神奇 K-D Tree(二维线段树)优化建图题。这里使用 K-D Tree。因为不会二维线段树

首先直接暴力连边会得到一个 \(O(n^2)\) 的 优秀 算法。考虑优化这个过程,由平面可以联想到 KDT。

建出 KDT 之后,我们用这颗树优化我们的建图过程。对于一个弹跳装置 \((p, t, L, R, D, U)\),我们从结点 \(p\) 开始,向在规定区域内代表的 KDT 上的结点连一条 \(t\) 长度的边。最后将 KDT 上的边的边权视为 \(0\),图就建完了。最后 Dijkstra 一波即可。时间复杂度 \(O(n\sqrt{n} \log n)\)。

这么做看似完美,但由于 128 MB 的空间限制无法直接 AC,因为边数是 \(O(m\sqrt{n})\) 的(KDT 复杂度)。但实质上我们并不需要在建好 KDT 后就大力连边,KDT只是帮助我们知道一个点可以到达什么点。而且这个神奇时间复杂度也很难卡过。

我们称原图中的点为『实点』,KDT 上的点为『虚点』。为方便起见,设实点 \(x\) 对应的虚点为 \(x+n\)。

在跑 Dijkstra 时,我们有如下算法:

  • 堆顶是实点:

    • 对于其一弹跳装置 \(y\),限定到达区域为 \(A\),在 KDT 上进行搜索,设当前虚点为 \(v\),那么按照 KDT 的套路:

      • 若子树 \(v \subseteq A\),直接松弛 \(v\);
      • 若子树 \(v \cap A = \varnothing\),跳出;
      • 若区域相交,先松弛实点 \(v - n\),然后递归。
  • 堆顶是虚点:
    • 松弛其对应的实点;
    • 松弛其在 KDT 上的左右儿子。

这样就达到绕开直接建图的目的。我们可以这样做的原因是,我们使用数据结构为工具,就已经能做到快速知道某个点可以到达的结点了,那么大力连边显然是冗余操作。

时间复杂度(STL 二叉堆) \(O((n+m)\log m + m\sqrt{n})\),空间只需要 \(O(n+m)\)。

Code

二叉堆

/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : NOI2019 弹跳
*/
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector> using namespace std;
const int N = 7e4 + 5;
const int V = N << 1;
const int K = 2; int n, m, w, h;
struct area {
int max[K], min[K];
};
inline bool inside(area x, area y) { // check if y is inside x.
for (int i = 0; i < K; i++)
if (y.min[i] < x.min[i] || y.max[i] > x.max[i])
return false;
return true;
}
inline bool separated(area x, area y) { // check if x and y is separated.
for (int i = 0; i < K; i++)
if (x.min[i] > y.max[i] || x.max[i] < y.min[i])
return true;
return false;
} struct point {
int dat[K];
inline int& operator [] (int p) {
return dat[p];
}
inline area toArea() {
return area{{dat[0], dat[1]}, {dat[0], dat[1]}};
}
};
pair<point, int> city[N];
int imag[N]; namespace KDT {
struct Node {
int lc, rc;
point p;
int max[K], min[K];
int vtxid;
inline int& operator [] (int d) {
return p[d];
}
inline area limit() {
return area{{max[0], max[1]}, {min[0], min[1]}};
}
} t[N];
int total = 0; #define lc(x) t[x].lc
#define rc(x) t[x].rc inline void pushup(int x) {
for (int i = 0; i < K; i++) {
t[x].max[i] = t[x].min[i] = t[x][i];
if (lc(x)) {
t[x].max[i] = max(t[x].max[i], t[lc(x)].max[i]);
t[x].min[i] = min(t[x].min[i], t[lc(x)].min[i]);
}
if (rc(x)) {
t[x].max[i] = max(t[x].max[i], t[rc(x)].max[i]);
t[x].min[i] = min(t[x].min[i], t[rc(x)].min[i]);
}
}
} namespace slctr {
int dim;
inline bool comp(pair<point, int>& a, pair<point, int>& b) {
return a.first[dim] < b.first[dim];
}
}
int build(pair<point, int>* a, int l, int r, int d) {
if (l > r) return 0;
int mid = (l + r) >> 1, x = ++total; slctr::dim = d;
nth_element(a + l, a + mid + 1, a + r + 1, slctr::comp);
t[x].p = a[mid].first, imag[t[x].vtxid = a[mid].second] = x; lc(x) = build(a, l, mid - 1, d ^ 1);
rc(x) = build(a, mid + 1, r, d ^ 1);
return pushup(x), x;
}
}; // namespace KDT
int root; struct edge {
area to;
int len;
};
vector<edge> G[N]; namespace SSSP {
struct heapNode {
int pos, dis;
inline bool operator < (const heapNode& x) const {
return dis > x.dis;
}
}; priority_queue<heapNode> pq;
int dist[V]; bool book[V]; inline void relax(int x, int w) {
if (dist[x] > w) pq.push(heapNode{x, dist[x] = w});
}
void search(int w, area a, int x = root) {
if (!x) return;
using namespace KDT;
area cur = t[x].limit();
if (separated(a, cur)) return;
if (inside(a, cur)) return relax(t[x].vtxid + n, w);
if (inside(a, t[x].p.toArea())) relax(t[x].vtxid, w);
search(w, a, lc(x)), search(w, a, rc(x));
} void Dijkstra(int src) {
memset(dist, 0x3f, sizeof(dist));
memset(book, false, sizeof(book));
pq.push(heapNode{src, dist[src] = 0});
while (!pq.empty()) {
int x = pq.top().pos; pq.pop();
if (book[x]) continue;
book[x] = true;
if (x > n) {
using namespace KDT;
relax(x - n, dist[x]);
if (lc(imag[x - n])) relax(t[lc(imag[x - n])].vtxid + n, dist[x]);
if (rc(imag[x - n])) relax(t[rc(imag[x - n])].vtxid + n, dist[x]);
} else {
for (auto y : G[x])
search(y.len + dist[x], y.to);
}
}
}
} signed 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].first[0], &city[i].first[1]);
city[i].second = i;
}
for (int i = 1; i <= m; i++) {
area to; int from, len;
scanf("%d%d", &from, &len);
scanf("%d%d", &to.min[0], &to.max[0]);
scanf("%d%d", &to.min[1], &to.max[1]);
G[from].push_back(edge{to, len});
} root = KDT::build(city, 1, n, 0);
SSSP::Dijkstra(1); for (int i = 2; i <= n; i++)
printf("%d\n", SSSP::dist[i]);
return 0;
}

【NOI2019】弹跳(KDT优化建图)的更多相关文章

  1. P5471- K-D tree优化建图-弹跳

    P5471- K-D tree优化建图-弹跳 优化建图是一种思想. 题意 有\(n\)个城市分布在小鸟岛上,有\(m\)个弹弓分布在这些城市里.因为弹弓体积大,固定麻烦,所以每个弹弓只能把小鸟弹飞到一 ...

  2. [十二省联考2019]字符串问题——后缀自动机+parent树优化建图+拓扑序DP+倍增

    题目链接: [十二省联考2019]字符串问题 首先考虑最暴力的做法就是对于每个$B$串存一下它是哪些$A$串的前缀,然后按每组支配关系连边,做一遍拓扑序DP即可. 但即使忽略判断前缀的时间,光是连边的 ...

  3. [SDOI2017]天才黑客[最短路、前缀优化建图]

    题意 一个 \(n\) 点 \(m\) 边的有向图,还有一棵 \(k\) 个节点的 trie ,每条边上有一个字符串,可以用 trie 的根到某个节点的路径来表示.每经过一条边,当前携带的字符串就会变 ...

  4. BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan

    Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...

  5. 【LibreOJ】#6354. 「CodePlus 2018 4 月赛」最短路 异或优化建图+Dijkstra

    [题目]#6354. 「CodePlus 2018 4 月赛」最短路 [题意]给定n个点,m条带权有向边,任意两个点i和j还可以花费(i xor j)*C到达(C是给定的常数),求A到B的最短距离.\ ...

  6. CF1007D. Ants(树链剖分+线段树+2-SAT及前缀优化建图)

    题目链接 https://codeforces.com/problemset/problem/1007/D 题解 其实这道题本身还是挺简单的,这里只是记录一下 2-SAT 的前缀优化建图的相关内容. ...

  7. 【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流

    [BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一 ...

  8. 洛谷P3783 [SDOI2017]天才黑客(前后缀优化建图+虚树+最短路)

    题面 传送门 题解 去看\(shadowice\)巨巨写得前后缀优化建图吧 话说我似乎连线段树优化建图的做法都不会 //minamoto #include<bits/stdc++.h> # ...

  9. 【ARC069F】Flags 2-sat+线段树优化建图+二分

    Description ​ 数轴上有 n 个旗子,第 ii 个可以插在坐标 xi或者 yi,最大化两两旗子之间的最小距离. Input ​ 第一行一个整数 N. ​ 接下来 N 行每行两个整数 xi, ...

随机推荐

  1. 378. Kth Smallest Element in a Sorted Matrix(大顶堆、小顶堆)

    Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth ...

  2. 1. 线性DP 53. 最大子序和.

    53. 最大子序和. https://leetcode-cn.com/problems/maximum-subarray/ func maxSubArray(nums []int) int { dp ...

  3. appium 数据参数化 登录模块

    下面是我最近学习的PYTHON的登录代码: class test(object): def getdic(self): d = {'username': '13', 'password': '1111 ...

  4. Java并发编程 - Runnbale、Future、Callable 你不知道的那点事(二)

    Java并发编程 - Runnbale.Future.Callable 你不知道的那点事(一)大致说明了一下 Runnable.Future.Callable 接口之间的关系,也说明了一些内部常用的方 ...

  5. 精尽MyBatis源码分析 - SQL执行过程(二)之 StatementHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  6. 如何在FL Studio中使用自动剪辑(下)

    在上集中我想大家介绍了FL Stduio Automation Clip的创建.播放列表操作及包络线类型介绍,在这篇文章中我将会给大家介绍如何在播放列表中使用Automation,剪辑通道的操作及使用 ...

  7. 如何合理的安排Folx的下载任务

    搭配使用Folx专业版的智能速控与任务计划功能,用户可以实现更加自动化.智能化的下载功能.通过使用任务计划功能,用户以时间表的方式安排Folx的下载任务:而智能速控的功能又能确保用户在下载的同时,有足 ...

  8. 如何使用OCR编辑器检查和识别文本

    ABBYY FineReader 15(Windows系统)中的OCR编辑器能帮助用户对扫描仪或者数码相机获取的图像文件进行自动文本识别,OCR区域绘制等,使这些图像文件能进一步转换为可编辑的格式.其 ...

  9. web自动化测试,弹出窗的操作

    弹出窗有两种: 1.alert弹窗 2.页面弹出窗 什么是alert弹窗呢,点击某一个事件后,会弹出一个弹窗,如下图所示,相信大家在测试中有遇到过,怎么操作它呢 1.1弹窗出现后,使用switch_t ...

  10. thinkphp3.2 添加自定义类似__ROOT__的变量

    1 thinkphp3.2 添加自定义类似__ROOT__的变量 2 3 在config.php文件中 4 return array( 5 '' => '', 6 'TMPL_PARSE_STR ...