携程笔试 携程秋招 0904
笔试时间:2025年9月4日
往年笔试合集:
第一题:运行时速
已知三类列车的运行时速区间如下(单位: km/h):
- 高铁: [250,350];
- 动车: [160,250];
- 城际列车: [200,300]。
给定某列车的运行时速v,请判断该车可能属于以上哪几类。由于区间存在交叠,v可能同时属于多类;若不属于任意一类,则认为是“其它”。
输入描述
每个测试文件均包含多组测试数据。
第一行输入一个整数T(1 ≤ T ≤ 2 × 10^5)表示数据组数,每组测试数据描述如下:
每行输入一个整数v(0 ≤ v ≤ 400)表示当前列车时速(单位: km/h)。
输出描述
对每组测试数据,新起一行输出该车可能的类别;若同时属于多类,则按以下固定顺序以单个空格分隔输出全部命中的类别:
- 高铁,输出G;
- 动车,输出D;
- 城际列车,输出C。
若不属于任意一类,则输出 other。区间两端点均计入 (闭区间)。
样例输入
6
250
170
120
300
351
200
样例输出
G D C
D
other
G C
other
D C
参考题解
处理海量输入/输出 (I/O) 瓶颈:由于测试数据量巨大(T 可达 20 万),使用 Scanner 读取输入和在循环内频繁使用 System.out.println 输出会导致超时。解决方案:采用 BufferedReader 加快输入速度,并使用一个 StringBuilder 收集所有输出结果,在所有计算结束后一次性打印,以大幅提升 I/O 效率。实现分类逻辑:对每一个输入的运行速度 v,使用独立的 if 语句来判断它是否落在高铁 (G)、动车 (D) 和城际列车 (C) 各自的速度区间内。将所有匹配到的类别代号(G, D, C)按固定顺序追加到一个临时的字符串中,并用空格隔开。如果没有任何区间匹配,则最终结果为 "other"。
C++:
#include <iostream> #include <string> #include <sstream> using namespace std; int main() { int T; cin >> T; stringstream output; for (int i = 0; i < T; i++) { int v; cin >> v; stringstream result; if (v >= 250 && v <= 350) { result << "G"; } if (v >= 160 && v <= 250) { if (result.str().length() > 0) { result << " "; } result << "D"; } if (v >= 200 && v <= 300) { if (result.str().length() > 0) { result << " "; } result << "C"; } if (result.str().length() == 0) { output << "other\n"; } else { output << result.str() << "\n"; } } cout << output.str(); return 0; }
Java:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; publicclass Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int T = Integer.parseInt(br.readLine()); StringBuilder output = new StringBuilder(); for (int i = 0; i < T; i++) { int v = Integer.parseInt(br.readLine()); StringBuilder result = new StringBuilder(); if (v >= 250 && v <= 350) { result.append("G"); } if (v >= 160 && v <= 250) { if (result.length() > 0) { result.append(" "); } result.append("D"); } if (v >= 200 && v <= 300) { if (result.length() > 0) { result.append(" "); } result.append("C"); } if (result.length() == 0) { output.append("other\n"); } else { output.append(result.toString()).append("\n"); } } System.out.print(output.toString()); } }
Python:
import sys def main(): T = int(sys.stdin.readline()) output = [] for _ in range(T): v = int(sys.stdin.readline()) result = [] if 250 <= v <= 350: result.append("G") if 160 <= v <= 250: result.append("D") if 200 <= v <= 300: result.append("C") if len(result) == 0: output.append("other") else: output.append(" ".join(result)) for line in output: print(line) if __name__ == "__main__": main()
第二题:排列修补
游游有一个原始排列,但部分元素丢失,剩余元素形成长度为n的数组{a1,a2,...,an}。
对于每个1 ≤ i ≤ n,将前缀{a1,a2,...,ai}看作新的数组,要把它补全成一个排列,至少需要插入多少个元素?换句话说,对于每个1 ≤ i ≤ n,考虑由前缀a1,a2,...,ai构成的元素集合。要将这个集合补全为一个长度为m的排列(即包含1,2,...,m),m的值必须不小于该集合中的任何元素)。为了使插入的元素数量最少,你需要选择一个最优的m。请问在这种最优选择下,至少需要插入多少个元素?
请输出每个前缀所需插入的最少元素数。
【名词解释】
长度为n的排列:由1,2,...,n这n个整数、按任意顺序组成的数组(每个整数均恰好出现一次)。例如,{2,3,1,5,4}是一个长度为5的排列,而{1,2,2}和{1,3,4}都不是排列,因为前者存在重复元素,后者包含了超出范围的数。
输入描述
第一行输入一个整数n(1 ≤ n ≤ 2 × 10^5),表示数组的长度;
第二行输入n个整数a1,a2,...,an(1 ≤ ai ≤ 10^9),表示数组的元素。
输出描述
在一行上输出n个整数,用空格分隔。其中第i个整数表示前缀{a1,a2,...,ai}需要插入的最少元素数。
样例输入
3
3 1 6
样例输出
2 1 3
参考题解
依次遍历输入的数组,对于每一个元素,都考虑从数组开头到该元素的“前缀”子数组。要将这个前缀补充为一个完整的 1 到 m 的排列,最优的 m 值必须等于当前前缀中的最大元素值。因此,对于每个前缀,我们只需要实时维护两个关键信息:当前前缀中所有元素的最大值。当前前缀中不同元素的个数。最少需要插入的元素数量,就是 “当前最大值” 与 “当前不同元素的个数” 两者之差。在遍历过程中,使用一个变量来更新最大值,同时使用一个集合(Set)来高效地统计不同元素的数量。
C++:
#include <iostream> #include <set> #include <sstream> using namespace std; int main() { int n; cin >> n; set<int> uniqueElements; // set 默认就是有序的,类似 TreeSet int maxElement = 0; stringstream sb; for (int i = 0; i < n; i++) { int currentElement; cin >> currentElement; uniqueElements.insert(currentElement); if (currentElement > maxElement) { maxElement = currentElement; } int insertions = maxElement - uniqueElements.size(); sb << insertions; if (i < n - 1) { sb << " "; } } cout << sb.str() << endl; return 0; }
Java:
import java.util.Scanner; import java.util.Set; import java.util.TreeSet; publicclass Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); // 使用 TreeSet 替代 HashSet,确保操作的时间复杂度为 O(log n) Set<Integer> uniqueElements = new TreeSet<>(); int maxElement = 0; // 使用 StringBuilder 优化I/O性能 StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { int currentElement = sc.nextInt(); uniqueElements.add(currentElement); if (currentElement > maxElement) { maxElement = currentElement; } int insertions = maxElement - uniqueElements.size(); sb.append(insertions); if (i < n - 1) { sb.append(" "); } } System.out.println(sb.toString()); sc.close(); } }
Python:
n = int(input()) unique_elements = set() max_element = 0 result = [] elements = list(map(int, input().split())) for i in range(n): current_element = elements[i] unique_elements.add(current_element) if current_element > max_element: max_element = current_element insertions = max_element - len(unique_elements) result.append(str(insertions)) print(" ".join(result))
第三题:数字个数
游游有四个整数l, r, k, x,求区间[l, r]中有多少个整数i满足:i (mod k) = x。
输入描述
每个测试文件均包含多组测试数据。
第一行输入一个整数T(1 ≤ T ≤ 10^4)代表数据组数,每组测试数据描述如下:
在一行上输入四个整数l, r, k, x(1 ≤ l ≤ r ≤ 10^9; 1 ≤ k ≤ 10^9; 0 ≤ x ≤ k - 1),表示查询的区间、模数、余数。
输出描述
对于每一组测试数据,新起一行给出一个整数,表示符合条件的数字个数。
样例输入
3
1 5 2 1
10 20 3 2
1 114514 2 0
样例输出
3
4
57257
参考题解
这个问题的核心是计算一个给定区间 [l, r] 内,有多少个整数 i 满足 i mod k = x 的条件。
1. 转化问题:直接在 [l, r] 区间内计数比较麻烦。一个常用的技巧是利用前缀和(或称为容斥原理)的思想。我们不直接求 [l, r] 的解,而是先求出在 [1, r] 区间内满足条件的整数个数,再减去在 [1, l-1] 区间内满足条件的整数个数,二者的差就是 [l, r] 区间内的解。
2. 核心函数:因此,问题被简化为实现一个函数 count(n, k, x),用于计算在 [1, n] 区间内有多少整数 i 满足 i mod k = x。
3. 求解 count(n, k, x):满足 i mod k = x 的数是一个公差为 k 的等差数列,首项是 x(如果 x 大于 0)或 k(如果 x 等于 0)我们可以通过整数除法来快速计算出在 [1, n] 范围内有多少个这样的数。例如,要计算 [1, n] 中有多少个数模 k 等于 x (x>0),这些数是 x, x+k, x+2k, …。我们只需要找到小于等于 n 的最大项 x + m*k,然后计算 m 的可能取值个数(从 0 开始),这可以通过 (n - x) / k + 1 来计算。对于 x=0 的特殊情况,这些数是 k, 2k, 3k, …,其个数就是 n / k。
通过上述方法,代码先定义了 count 函数,然后在主循环中对于每个查询 l, r, k, x,通过调用 count(r, k, x) - count(l - 1, k, x) 来得到最终结果。
C++:
#include <iostream> using namespace std; long long count(long long n, long long k, long long x) { if (n < 0) { return 0; } if (x == 0) { return n / k; } else { if (n < x) { return 0; } return (n - x) / k + 1; } } int main() { int T; cin >> T; while (T-- > 0) { long long l, r, k, x; cin >> l >> r >> k >> x; long long result = count(r, k, x) - count(l - 1, k, x); cout << result << endl; } return 0; }
Java:
import java.util.Scanner; publicclass Main { public static long count(long n, long k, long x) { if (n < 0) { return0; } if (x == 0) { return n / k; } else { if (n < x) { return0; } return (n - x) / k + 1; } } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int T = sc.nextInt(); while (T-- > 0) { long l = sc.nextLong(); long r = sc.nextLong(); long k = sc.nextLong(); long x = sc.nextLong(); long result = count(r, k, x) - count(l - 1, k, x); System.out.println(result); } sc.close(); } }
Python:
def count(n, k, x): if n < 0: return 0 return n // k if x == 0 else ((n - x) // k + 1 if n >= x else 0) T = int(input()) for _ in range(T): l, r, k, x = map(int, input().split()) print(count(r, k, x) - count(l - 1, k, x))
第四题:K-小距离
给定一棵包含 n 个节点的无向带权树,每条边 (u,v) 的权重为正整数 wₙᵥ。树上共有 m 个标记为 “红点” 的节点。给定一个整数 K (1 ≤ K ≤ m),对于每个节点 v,定义该节点到树中 K 个最近红点的距离和:F (v) = Σᵢ₌₁ᴷ dᵢ(v),其中 d₁(v) ≤ d₂(v) ≤ … ≤ d_K (v) 是节点 v 到所有红点按距离从小到大排序的前 K 个距离。
请计算所有节点的 F (v)。
输入描述
第一行输入三个整数 n, m, K (2 ≤ m ≤ n ≤ 2×10⁵, 1 ≤ K ≤ min (100, m)),分别表示节点数、红点数和 K。
第二行输入 m 个互不相同的整数 r₁, r₂, …, r_m (1 ≤ rᵢ ≤ n),表示红点节点编号。
接下来 n - 1 行,每行输入三个整数 uᵢ, vᵢ, wᵢ(1 ≤ uᵢ, vᵢ ≤ n, uᵢ ≠ vᵢ, 1 ≤ wᵢ ≤ 10⁶),表示一条无向带权边。
保证输入构成一棵树。
输出描述
输出一行,包含 n 个整数 F (1), F (2), …, F (n),用空格分隔。
样例输入
5 3 1
2 4 5
1 2 1
2 3 1
3 4 1
4 5 1
样例输出
1 0 1 0 0
参考题解
核心解决思路是**树形动态规划 (Tree DP
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南