为了更好的阅读体检,为了更好的阅读体检,,可以查看我的算法学习博客第三题-交通规划
在线评测链接:P1237
题目内容
塔子哥所在的国家有 n 个城市,这 n 个城市排成一列,按顺序编号为 1,2,3,...,n。然而,由于历史原因和地理条件等多种原因,这些城市之间并没有相互连接的铁路,导致交通十分不便。
为了改善这种情况,政府决定修建一些铁路来提高城市之间的交通效率。具体来说,政府计划在未来的 T 天内进行一系列铁路建设工作。每一天,政府会进行如下操作之一:
-
L x
:在编号为 x 的城市和其左边的城市之间修建一条铁路,以便两个城市之间的交通更加便利。如果 x 已经位于最左边,或者 x 和它左边的城市之间已经存在铁路,则该操作无效。 -
R x
:在编号为 x 的城市和其右边的城市之间修建一条铁路,以便两个城市之间的交通更加便利。如果 x 已经位于最右边,或者 x 和它右边的城市之间已经存在铁路,则该操作无效。 -
Q x
:查询从编号为 x 的城市出发,最远能够到达的向左和向右的城市的编号。
塔子哥需要编写一段程序来模拟这一系列操作,并及时输出每个 Q x
操作的结果。通过这个程序,政府可以更加高效地规划城市之间的交通网络,从而促进经济和社会的发展。
输入描述
第一行输入两个正整数 n , T ; 接下来 T 行,每行输入形如题面中的其中一种。
, ,
输出描述
对于每一个Q x
操作,输出一行两个正整数,分别表示 x 往左边和往右边最远能到达的城市编号中间用空格隔开。
样例
输入
3 5 Q 2 L 2 Q 2 R 2 Q 2
输出
2 2 1 2 1 3
思路
并查集
考虑将n个城市看成图上的n个节点。那么
-
每次将两个城市之间连通,相当于在点 u 和点 v 之间连一条边
-
每次查询一个城市可以向左和向右到达的最远城市,这启发我们并查集合并时需要维护两个值,一个合并时总是指向值更小的,一个合并时总是指向值更大的。即维护集合最大最小值
知识点学习:并查集
完全没接触过或不太熟悉并查集的同学,可以参考以下学习路线:
1.并查集算法讲解 -C++实现
图论——并查集(详细版)
看完本视频之后对并查集先有一个初步的了解
2.刷题
根据塔子哥给出的类似题目推荐,刷完Leetcode的相关题目以及CodeFun2000相关题目推荐
3.进阶应用(偏竞赛难度)
如果对并查集的应用有比较大的兴趣,希望进一步接触更多更妙的内容,可以学习
1.OI-WIKI-并查集应用
2.百度搜集ATC,CF以及ICPC/CCPC区域赛真题
类似题目推荐
Leetcode
-
547. 朋友圈
-
130. 被围绕的区域
-
200. 岛屿数量
-
684. 冗余连接
-
323. 无向图中连通分量的数目
CodeFun2000
以下问题用BFS/DFS解决都能解决。为了更好的学习并查集,希望大家能够尽量使用并查集完成
1.P1147 阿里巴巴 2023.4.3-研发岗-第二题-又一个连通块数数题
2.P1094. 米哈游 2023.03.19-第一题-塔子哥的RBG矩阵
时间复杂度:O(n\log n)
代码
CPP
#include <bits/stdc++.h> using namespace std; const int N = 10010; // pL:维护集合最小值 , pR:最大值 int pL[N], pR[N]; int n, m; char op[2]; int num; // 并查集 非递归写法 int find(int p[], int x) { int root = x; while (root != p[root]) root = p[root]; while (x != p[x]) { int nx = p[x]; p[x] = root; x = nx; } return root; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) pL[i] = pR[i] = i; // 模拟 for (int i = 1; i <= m; ++i) { scanf("%s%d", op, &num); if (*op == 'L') { if (num == 1) continue; int pa = find(pL, num - 1), pb = find(pL, num); if (pa == pb) continue ; pL[pb] = pa; pa = find(pR, num - 1), pb = find(pR, num); pR[pa] = pb; } else if (*op == 'R') { if (num == n) continue ; int pa = find(pL, num), pb = find(pL, num + 1); if (pa == pb) continue ; pL[pb] = pa; pa = find(pR, num), pb = find(pR, num + 1); pR[pa] = pb; } else { printf("%d %d\n", find(pL, num), find(pR, num)); } } return 0; }
Java
import java.util.Objects; import java.util.Scanner; public class Main { static int N = 100010; // pL:维护集合最小值 , pR:最大值 static int[] pL = new int[N]; static int[] pR = new int[N]; static Scanner sc = new Scanner(System.in); // 并查集 : 寻找父亲 static int find(int[] p, int x) { if (x != p[x]) p[x] = find(p, p[x]); return p[x]; } public static void main(String[] args) { int n = sc.nextInt(); int m = sc.nextInt(); for (int i = 1; i <= n; ++i) pL[i] = pR[i] = i; // 模拟 for (int i = 0; i < m; ++i) { String op = sc.next(); int x = sc.nextInt(); if ("Q".equals(op)) { int L = find(pL, x); int R = find(pR, x); System.out.println(L + " " + R); } else if ("L".equals(op)) { if (x == 1) continue; int pa = find(pL, x - 1); int pb = find(pL, x); if (pa == pb) continue; pL[pb] = pa; pa = find(pR, x - 1); pb = find(pR, x); if (pa == pb) continue; pR[pa] = pb; } else { if (x == n) continue; int pa = find(pL, x); int pb = find(pL, x + 1); if (pa == pb) continue; pL[pb] = pa; pa = find(pR, x); pb = find(pR, x + 1); if (pa == pb) continue; pR[pa] = pb; } } } }
Python
n, T = map(int, input().split()) f = [i for i in range(n+1)] # mi维护集合最小值 , mx 维护 最大值 mi = [i for i in range(n+1)] mx = [i for i in range(n+1)] # 并查集 模板 def find(x): global f if f[x] == x: return x f[x] = find(f[x]) return f[x] def mer(x, y): global f x = find(x) y = find(y) if x != y: f[x] = y mx[y] = max(mx[x], mx[y]) mi[y] = min(mi[x], mi[y]) for ii in range(T): p, x = input().split() x = int(x) if p == 'L' and x != 1: mer(x, x-1) if p == 'R' and x != n: mer(x, x+1) if p == 'Q': x = find(x) print(mi[x], mx[x])
Go
package main import ( "fmt" ) var f []int var mi []int var mx []int func main() { var n, T int fmt.Scan(&n, &T) // f: 父亲 f = make([]int, n+1) // mi mx 维护每个点所在集合的最大值最小值 mi = make([]int, n+1) mx = make([]int, n+1) // 初始化 指向自己 for i := 0; i <= n; i++ { f[i] = i mi[i] = i mx[i] = i } // 模拟 for i := 0; i < T; i++ { var p string var x int fmt.Scan(&p, &x) if p == "L" && x != 1 { mer(x, x-1) } if p == "R" && x != n { mer(x, x+1) } if p == "Q" { x = find(x) fmt.Println(mi[x], mx[x]) } } } // 并查集 模板 func find(x int) int { if f[x] == x { return x } f[x] = find(f[x]) return f[x] } func mer(x, y int) { x = find(x) y = find(y) if x != y { f[x] = y mx[y] = max(mx[x], mx[y]) mi[y] = min(mi[x], mi[y]) } } // 最大最小值 func max(a, b int) int { if a > b { return a } return b } func min(a, b int) int { if a < b { return a } return b }
Js
process.stdin.resume(); process.stdin.setEncoding('utf-8'); let input = ''; process.stdin.on('data', (data) => { input += data; }); process.stdin.on('end', () => { lines = input.trim().split('\n'); let f = [] let mi = [] let mx = [] const arr = lines[0].split(' ') const n = parseInt(arr[0]) const T = parseInt(arr[1]) // mi mx 维护每个点所在集合的最大值最小值 for (let i = 0; i <= n; i++) { f[i] = i mi[i] = i mx[i] = i } // 并查集模板 function find(x) { if (f[x] === x) { return x } f[x] = find(f[x]) return f[x] } function mer(x, y) { x = find(x) y = find(y) if (x !== y) { f[x] = y mx[y] = Math.max(mx[x], mx[y]) mi[y] = Math.min(mi[x], mi[y]) } } // 模拟 for (let i = 1; i <= T; i++) { const line = lines[i].trim().split(' ') const p = line[0] const x = parseInt(line[1]) if (p === 'L' && x !== 1) { mer(x, x-1) } if (p === 'R' && x !== n) { mer(x, x+1) } if (p === 'Q') { const res = find(x) console.log(mi[res], mx[res]) } } });