#P4553. 第2题-坐火车Ⅱ
-
1000ms
Tried: 69
Accepted: 9
Difficulty: 6
所属公司 :
华为
时间 :2026年1月22日-非AI方向(通软&嵌软&测试&算法&数据科学)
-
算法标签>双指针
第2题-坐火车Ⅱ
解题思路
把每个乘客记录视为点 (time=J, train=I),每个查询是固定长度为 X 的时间窗 [S, S+X),要求统计窗口内出现过的火车编号种类数。
离线处理:
-
将所有乘客记录按时间
J升序排序。 -
将所有查询起点
S(带原下标)按S升序排序。 -
用双指针维护窗口
[S, S+X):- 左指针
l右移,删除time < S的事件 - 右指针
r右移,加入time < S+X的事件
- 左指针
-
用数组
cnt[train]统计某火车在窗口内出现次数,并维护distinct表示cnt>0的火车数量。 -
查询按
S递增时,l/r只单调移动,总更新次数O(M)。
相关算法:排序 + 双指针滑动窗口 + 计数数组。
复杂度分析
- 时间复杂度:
O(M log M + K log K) - 空间复杂度:
O(N + M + K)
代码实现
import sys
def count_trains_in_windows(n, x, starts, events):
# events: list of (time, train)
events.sort()
qs = [(s, idx) for idx, s in enumerate(starts)]
qs.sort()
cnt = [0] * n
distinct = 0
ans = [0] * len(starts)
l = 0
r = 0
m = len(events)
for s, idx in qs:
left = s
right = s + x # 左闭右开 [s, s+x)
# 移除 time < left 的事件
while l < m and events[l][0] < left:
t, train = events[l]
cnt[train] -= 1
if cnt[train] == 0:
distinct -= 1
l += 1
# 加入 time < right 的事件
while r < m and events[r][0] < right:
t, train = events[r]
if cnt[train] == 0:
distinct += 1
cnt[train] += 1
r += 1
ans[idx] = distinct
return ans
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
p = 0
n, x, k = data[p], data[p+1], data[p+2]
p += 3
starts = data[p:p+k]
p += k
m = data[p]
p += 1
events = []
for _ in range(m):
i = data[p]
j = data[p+1]
p += 2
events.append((j, i)) # (time, train)
res = count_trains_in_windows(n, x, starts, events)
sys.stdout.write(" ".join(map(str, res))) # 一行输出,用空格隔开
if __name__ == "__main__":
main()
#include <bits/stdc++.h>
using namespace std;
// 核心功能:统计每个窗口内出现过的火车数量
static vector<int> countTrainsInWindows(
int n, int x,
const vector<int>& starts,
vector<pair<int,int>>& events // (time, train)
) {
sort(events.begin(), events.end()); // 按时间升序
int k = (int)starts.size();
vector<pair<int,int>> qs; // (S, idx)
qs.reserve(k);
for (int i = 0; i < k; i++) qs.push_back({starts[i], i});
sort(qs.begin(), qs.end());
vector<int> cnt(n, 0); // 每辆火车在窗口内出现次数
int distinct = 0; // 当前窗口内出现过的火车数
vector<int> ans(k, 0);
int l = 0, r = 0;
int m = (int)events.size();
for (auto &q : qs) {
int s = q.first;
int idx = q.second;
int left = s;
int right = s + x; // 左闭右开 [S, S+X)
// 移除 time < left 的事件
while (l < m && events[l].first < left) {
int train = events[l].second;
cnt[train]--;
if (cnt[train] == 0) distinct--;
l++;
}
// 加入 time < right 的事件
while (r < m && events[r].first < right) {
int train = events[r].second;
if (cnt[train] == 0) distinct++;
cnt[train]++;
r++;
}
ans[idx] = distinct;
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, X, K;
cin >> N >> X >> K;
vector<int> starts(K);
for (int i = 0; i < K; i++) cin >> starts[i];
int M;
cin >> M;
vector<pair<int,int>> events;
events.reserve(M);
for (int i = 0; i < M; i++) {
int I, J;
cin >> I >> J;
events.push_back({J, I}); // (time, train)
}
vector<int> ans = countTrainsInWindows(N, X, starts, events);
// 一行输出,用空格隔开
for (int i = 0; i < K; i++) {
if (i) cout << " ";
cout << ans[i];
}
return 0;
}
import java.util.*;
public class Main {
// 事件:上车时间 + 火车编号
static class Event {
int time, train;
Event(int time, int train) {
this.time = time;
this.train = train;
}
}
// 查询:起点S + 原下标
static class Query {
int s, idx;
Query(int s, int idx) {
this.s = s;
this.idx = idx;
}
}
// 核心功能:统计每个窗口内出现过的火车数量
static int[] countTrainsInWindows(int n, int x, int[] starts, Event[] events) {
Arrays.sort(events, Comparator.comparingInt(a -> a.time));
int k = starts.length;
Query[] qs = new Query[k];
for (int i = 0; i < k; i++) qs[i] = new Query(starts[i], i);
Arrays.sort(qs, Comparator.comparingInt(a -> a.s));
int[] cnt = new int[n]; // 每辆火车在窗口内出现次数
int distinct = 0; // 当前窗口内出现过的火车数
int[] ans = new int[k];
int l = 0, r = 0, m = events.length;
for (Query q : qs) {
int left = q.s;
int right = q.s + x; // 左闭右开 [S, S+X)
// 移除 time < left 的事件
while (l < m && events[l].time < left) {
int train = events[l].train;
cnt[train]--;
if (cnt[train] == 0) distinct--;
l++;
}
// 加入 time < right 的事件
while (r < m && events[r].time < right) {
int train = events[r].train;
if (cnt[train] == 0) distinct++;
cnt[train]++;
r++;
}
ans[q.idx] = distinct;
}
return ans;
}
public static void main(String[] args) {
// 按要求:不使用快读快写,直接用 Scanner
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int x = sc.nextInt();
int k = sc.nextInt();
int[] starts = new int[k];
for (int i = 0; i < k; i++) starts[i] = sc.nextInt();
int m = sc.nextInt();
Event[] events = new Event[m];
for (int i = 0; i < m; i++) {
int train = sc.nextInt();
int time = sc.nextInt();
events[i] = new Event(time, train);
}
int[] ans = countTrainsInWindows(n, x, starts, events);
// 一行输出,用空格隔开
for (int i = 0; i < k; i++) {
if (i > 0) System.out.print(" ");
System.out.print(ans[i]);
}
sc.close();
}
}
题目内容
火车站有 n 辆火车,按 [0,n] 编号。现在有 n 名乘客提供了他们要乘坐的火车编号和计划上车的时间点。火车站的工作人员想基于这些信息统计在指定时间的时间区间内。有乘客上车的火车数量。为了简化,时间区间的长度总是为 X,火车站的工作人员会提供 K 个起始时间从而构成 K 个时间从而构成 K 个时间区间,现在请你统计在每个时间区间内有乘客上车的火车数量。
注意:时间区间会有重叠
输入描述
-
第 1 行:N X K,其中
- N 是火车数量、取值范围 [1,100000)
- X 是时间区间的跨度,取值范围是 [1,100000]
- K 是要统计的时间区间的个数,取值范围是 [1,100000]
-
第 2 行:S1S2...SK,总共 K 个数字,每个数字代表一个起始时间,取值范围是 [0,100000]
-
第 3 行:M,M 代表乘客数量,取值范围 [1,100000)
-
第 4 行:i,j,其中
- i 代表乘客要乘坐的火车编号,取值范围是 [0,n)
- j 代表乘客计划上车的时间,取值范围是 [0,100000)
-
_:格式同第 4 行
-
第 M+3 行:格式同第 4 行
输出描述
按给定的 K 个起始时间的输入顺序 依次输出每个时间区间内有乘客上车的火车数量,如给定的起始时间是 3 1 ,则先输出 3 为起始时间的结果,再输出 1 为起始时间的结果
样例1
输入
4 2 2
3 4
4
2 4
2 3
1 2
3 5
输出
1 2
说明
4 2 2 :总共有 4 辆火车,编号是 0,1,2,3,指定时间区间的跨度为 2 ,并有 2 个起始时间
3 4:起始时间分别是 3 4,时间区间分别是 [3,3+2),[4,4+2)
4:总共 4 名票客
2 4:乘客要搭乘火车 2,计划在 4 上车,在时间区间 [3,3+2) 和 [4,4+2) 内
2 3:乘客要搭乘火车 2,计划在 3 上车,在时间区间 [3,3+2) 内
1 2:乘客要搭乘火车 1,计划在 2 上车,不在指定的时间区内
3 5:乘客要搭乘火车 3,计划在 5 上车,在时间区间 [4,4+2) 内
可知
-
对于 [3,3+2),火车 2 有两名乘客上车
-
对于 [4,4+2),火车 2 和火车 3 都有乘客上车
因此最后输出 1 2
样例2
输入
4 5 3
20 10 30
3
3 32
0 14
1 30
输出
0 1 2