【深基15.例2】寄包柜

GitHub跳转原题关系图返回列表

为每个寄包柜维护一个稀疏字典,只保存实际写入过的格子编号和物品。

OJ: luogu

题目 ID: P3613

难度:普及-

标签:字典模拟python

日期: 2026-07-16 18:10

题意

有很多寄包柜,每个柜子的格子上界未知。支持给 (柜子,格子) 写入物品编号,以及查询该位置的物品。

思路

格子编号可能很大,但操作总数只有 10^5,没有必要为每个柜子开到最大编号。

建立 lockers[i] 字典,只记录第 i 个柜子实际写入过的 cell -> item。写入和查询都直接使用两次下标,期望 O(1)O(1) 完成。

Python 知识

  • [dict() for _ in range(n+1)] 为每个柜子创建独立字典;不能写 [{}]*(n+1),否则所有柜子会共享同一个字典。
  • lockers[locker][cell]=item 直接表达二维稀疏映射。
  • 操作行长度不同,按行读取后解包比整份 token 更直观。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/collections_toolkit.mddict 键值映射。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/cpp_to_python_pitfalls.md:可变容器不能用乘法复制。

代码

python
import sys


input = sys.stdin.buffer.readline
n, query_count = map(int, input().split())
lockers = [dict() for _ in range(n + 1)]
answers = []

for _ in range(query_count):
    operation = list(map(int, input().split()))
    if operation[0] == 1:
        _, locker, cell, item = operation
        lockers[locker][cell] = item
    else:
        _, locker, cell = operation
        answers.append(str(lockers[locker][cell]))

print("\n".join(answers))
cpp
/**
 * P3613 【深基15.例2】寄包柜
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-27 00:00
 * update_at: 2026-07-27 00:00
 */

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
const int MAXQ = 100005;

// 每个柜子用链表存(格子→物品)
// head[i] 指向柜子 i 的链表头
// to[cnt] 格子编号, val[cnt] 物品编号, nxt[cnt] 下一条
int head[MAXN], to[MAXQ], val[MAXQ], nxt[MAXQ];
int cnt = 0;

// 向柜子 locker 的链表头部插入 (格子,物品)
void add(int locker, int cell, int item) {
    ++cnt;
    to[cnt] = cell;
    val[cnt] = item;
    nxt[cnt] = head[locker];
    head[locker] = cnt;
}

// 查询柜子 locker 中格子 cell 上的物品
int query(int locker, int cell) {
    for (int i = head[locker]; i; i = nxt[i]) {
        if (to[i] == cell) return val[i];
    }
    return -1; // 题目保证查询都存在,不会走到这里
}

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    while (q--) {
        int op, locker, cell;
        scanf("%d%d%d", &op, &locker, &cell);
        if (op == 1) { // 写入
            int item;
            scanf("%d", &item);
            add(locker, cell, item);
        } else { // 查询
            printf("%d\n", query(locker, cell));
        }
    }
    return 0;
}

复杂度

每次操作期望 O(1)O(1),总空间与不同的实际写入格子数成正比,最坏 O(q)O(q)

总结

面对巨大但稀疏的二维编号空间,字典只存出现过的位置,通常比按最大下标开二维数组更自然。