题目链接

小红的慕斯模具统计

题目描述

小红经营着一家烘焙坊,共有 种款式的慕斯模具(编号 )。流水线上记录了 次模具的使用信息,第 次记录显示,在时间点 使用了编号为 的模具。 小红提出了 个查询。每个查询由起始时间 和固定时间跨度 组成,代表观察的时间区间为 (即从 开始持续 个单位时间)。 对于每个查询,需要计算在该时间区间内使用了多少种不同编号的模具。

解题思路

本题是一个区间不同元素计数问题。由于查询的时间跨度 是固定的,且所有查询区间具有相同的长度,我们可以通过**滑动窗口(双指针)**来高效解决。

  1. 数据组织与排序

    • 根据样例说明,记录的输入格式为 ID Time
    • 将所有使用记录 按照时间点 进行升序排序。
    • 将查询离线处理:将查询起始时间 与原始索引 绑定,并按照 进行升序排序。
  2. 滑动窗口逻辑

    • 使用两个指针 left_ptrright_ptr 维护当前窗口 内的使用记录。
    • 随着 增加,窗口的左右边界单调右移。
    • 右边界扩展:将所有满足 的记录加入窗口,并更新模具频次。
    • 左边界收缩:将所有满足 的记录移出窗口,并更新模具频次。
  3. 计数维护

    • 由于模具编号 可能非常大(高达 ),直接使用数组会越界。在 C++ 和 Java 中,我们使用映射表(mapHashMap)来记录窗口内每种模具的频次。
    • 使用变量 记录当前窗口内不同模具的种类数。

代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>

using namespace std;

// 记录结构体
struct Record {
    int t, id;
};

// 查询信息结构体
struct Query {
    int l, idx;
};

int main() {
    int n, w, q;
    cin >> n >> w >> q;

    vector<Query> queries(q);
    for (int i = 0; i < q; ++i) {
        int l;
        cin >> l;
        queries[i] = {l, i};
    }

    int m;
    cin >> m;
    vector<Record> records(m);
    for (int i = 0; i < m; ++i) {
        int id, t;
        // 注意:根据样例,输入格式为 ID Time
        cin >> id >> t;
        records[i] = {t, id};
    }

    // 预处理:按时间排序
    sort(records.begin(), records.end(), [](const Record& a, const Record& b) {
        return a.t < b.t;
    });
    sort(queries.begin(), queries.end(), [](const Query& a, const Query& b) {
        return a.l < b.l;
    });

    vector<int> ans(q);
    map<int, int> cnt; // 使用 map 处理可能的大 ID
    int distinct_count = 0;
    int left_ptr = 0, right_ptr = 0;

    for (int i = 0; i < q; ++i) {
        int L = queries[i].l;
        int R = L + w - 1;

        // 移入右边界
        while (right_ptr < m && records[right_ptr].t <= R) {
            int mid = records[right_ptr].id;
            if (cnt[mid] == 0) distinct_count++;
            cnt[mid]++;
            right_ptr++;
        }
        // 移出左边界
        while (left_ptr < m && records[left_ptr].t < L) {
            int mid = records[left_ptr].id;
            cnt[mid]--;
            if (cnt[mid] == 0) distinct_count--;
            left_ptr++;
        }
        ans[queries[i].idx] = distinct_count;
    }

    for (int i = 0; i < q; ++i) {
        cout << ans[i] << (i == q - 1 ? "" : " ");
    }
    cout << endl;

    return 0;
}
import java.util.*;

public class Main {
    static class Record {
        int t, id;
        Record(int t, int id) {
            this.t = t;
            this.id = id;
        }
    }

    static class Query {
        int l, idx;
        Query(int l, int idx) {
            this.l = l;
            this.idx = idx;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int w = sc.nextInt();
        int q = sc.nextInt();

        Query[] queries = new Query[q];
        for (int i = 0; i < q; i++) {
            queries[i] = new Query(sc.nextInt(), i);
        }

        int m = sc.nextInt();
        Record[] records = new Record[m];
        for (int i = 0; i < m; i++) {
            int id = sc.nextInt();
            int t = sc.nextInt();
            records[i] = new Record(t, id);
        }

        // 排序
        Arrays.sort(records, (a, b) -> Integer.compare(a.t, b.t));
        Arrays.sort(queries, (a, b) -> Integer.compare(a.l, b.l));

        int[] ans = new int[q];
        Map<Integer, Integer> cnt = new HashMap<>();
        int distinctCount = 0;
        int leftPtr = 0;
        int rightPtr = 0;

        for (int i = 0; i < q; i++) {
            int L = queries[i].l;
            int R = L + w - 1;

            while (rightPtr < m && records[rightPtr].t <= R) {
                int id = records[rightPtr].id;
                int currentCnt = cnt.getOrDefault(id, 0);
                if (currentCnt == 0) distinctCount++;
                cnt.put(id, currentCnt + 1);
                rightPtr++;
            }
            while (leftPtr < m && records[leftPtr].t < L) {
                int id = records[leftPtr].id;
                int currentCnt = cnt.get(id);
                if (currentCnt == 1) {
                    distinctCount--;
                    cnt.remove(id);
                } else {
                    cnt.put(id, currentCnt - 1);
                }
                leftPtr++;
            }
            ans[queries[i].idx] = distinctCount;
        }

        for (int i = 0; i < q; i++) {
            System.out.print(ans[i] + (i == q - 1 ? "" : " "));
        }
        System.out.println();
    }
}
def solve():
    # 输入读取
    line1 = input().split()
    n, w, q = map(int, line1)
    
    l_list = list(map(int, input().split()))
    queries = []
    for i in range(q):
        queries.append([l_list[i], i])
    
    m = int(input())
    records = []
    for _ in range(m):
        # 样例说明表明输入格式为 ID Time
        mid, t = map(int, input().split())
        records.append([t, mid])
    
    # 离线处理:按时间排序
    records.sort()
    queries.sort()
    
    ans = [0] * q
    cnt = {} # 使用字典处理可能的大 ID
    distinct_count = 0
    left_ptr = 0
    right_ptr = 0
    
    for l, idx in queries:
        r = l + w - 1
        
        # 移动右指针扩展窗口
        while right_ptr < m and records[right_ptr][0] <= r:
            mid = records[right_ptr][1]
            if cnt.get(mid, 0) == 0:
                distinct_count += 1
            cnt[mid] = cnt.get(mid, 0) + 1
            right_ptr += 1
            
        # 移动左指针收缩窗口
        while left_ptr < m and records[left_ptr][0] < l:
            mid = records[left_ptr][1]
            cnt[mid] -= 1
            if cnt[mid] == 0:
                distinct_count -= 1
            left_ptr += 1
            
        ans[idx] = distinct_count
        
    print(*(ans))

solve()

算法及复杂度

  • 算法:滑动窗口(双指针) + 离线查询。
  • 时间复杂度:。排序占主导地位,滑动窗口过程为线性扫过记录和查询列表。
  • 空间复杂度:。需要存储记录、查询及每种模具的频次。