洛谷P6348 [PA2011]Journeys(并查集)
洛谷P6348 [PA2011]Journeys(并查集)
题目描述
一个星球上有 \(n\) 个国家和许多双向道路,国家用 \(1\sim n\) 编号。
但是道路实在太多了,不能用通常的方法表示。于是我们以如下方式表示道路:\((a,b),(c,d)\) 表示,对于任意两个国家 \(x,y\),如果 \(a\le x\le b,c\le y\le d\),那么在 \(x,y\) 之间有一条道路。
首都位于 \(P\) 号国家。你想知道 \(P\) 号国家到任意一个国家最少需要经过几条道路。保证 \(P\) 号国家能到任意一个国家。
数据范围
对于所有测试点,保证 \(1\le n\le 5\times 10^5\),\(1\le m\le 10^5\),\(1\le a\le b\le n\),\(1\le c\le d\le n\)。
解题思路
当然这是一道线段树优化建图模板题,但我们有更简单更快的做法
请想象一下超级暴力 bfs 的过程,我们从 s 点开始 bfs,扫一遍找到包含 s 的区间,从而轻松找到和 s 距离为 1 的点,我们再用距离为 1 的点找到距离为 2 的点
这样显然很暴力,但可喜的是,我们有些地方还可以优化
- bfs 过程中每个点只被更新一次且只更新别人一次
- 需要快速找到包含 s 的区间,并删之
那么第一个优化我们直接用并查集就可以维护,这是经典套路不再多说
第二个优化使用线段树,将区间挂在线段树上,询问时直接把根节点到对应叶子节点上挂的区间全部扔到队列中并删除
总复杂度 \(\Theta((n+m)log_n\) 温馨提示:最好不要用 vector 。目前是最优解
const int M = 200500;
const int N = 500500;
int m, n, s;
int l1[M], r1[M], l2[M], r2[M];
bool vis[N]; int dis[N], f[N];
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
#define ls p << 1
#define rs ls | 1
struct node {
int l, r, k;
}q[N];
int h[N<<2], ne[N<<4], to[N<<4], tot;
inline void add(int x, int y) {
ne[++tot] = h[x], to[h[x] = tot] = y;
}
void change(int p, int l, int r, int L, int R, int k) {
if (L <= l && r <= R) return add(p, k);
int mid = (l + r) >> 1;
if (L <= mid) change(ls, l, mid, L, R, k);
if (mid < R) change(rs, mid + 1, r, L, R, k);
}
int L, R;
void update(int p, int l, int r, int x, int k) {
if (h[p]) {
for (int i = h[p]; i; i = ne[i]) {
int tp = to[i];
if (!vis[tp]) q[++R] = ((node){l2[tp], r2[tp], k}), vis[tp] = 1;
}
h[p] = 0;
}
if (l == r) return;
int mid = (l + r) >> 1;
if (x <= mid) update(ls, l, mid, x, k);
else update(rs, mid + 1, r, x, k);
}
void bfs(int s) {
L = 1, R = 0;
q[++R] = ((node){s, s, 0});
while (L <= R) {
node x = q[L++];
for (int i = find(x.l); i <= x.r; i = find(i + 1))
update(1, 1, n, i, x.k + 1), dis[i] = x.k, f[i] = i + 1;
}
}
int main() {
read(n), read(m), read(s);
for (int i = 1;i <= m; i++) {
read(l1[i]), read(r1[i]), read(l2[i]), read(r2[i]);
change(1, 1, n, l1[i], r1[i], i);
l1[i + m] = l2[i], r1[i + m] = r2[i];
l2[i + m] = l1[i], r2[i + m] = r1[i];
change(1, 1, n, l2[i], r2[i], i + m);
}
for (int i = 1;i <= n + 1; i++) f[i] = i; bfs(s);
for (int i = 1;i <= n; i++) write(dis[i]);
return 0;
}