牛半仙的妹子串

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

读入时按名字结尾字母分 26 个桶,桶内按(评分降序、同分先读入在前)排好序,询问直接取桶内第 k 个名字。

OJ: roj

题目 ID: 10008

难度:普及-

标签:排序分桶模拟

创建: 2026-10-02 17:36

更新: 2026-10-07 12:15

题目描述

牛半仙有 nn 个妹子,每个妹子有一个名字(只含小写字母、长度不超过 50)和一个评分。他要回答 mm 个询问 (x,k)(x, k):名字以字母 xx 结尾的妹子中评分第 kk 大的妹子的名字,评分相同时先读入的算评分更大;这样的妹子不足 kk 个时输出 Orz YYR tql。输入第一行 n,mn, m,接下来 nn 行每行一个名字和评分,接下来 mm 行每行一个字母 xx 和正整数 kk。n,m⩽105n, m \leqslant 10^5,k⩽nk \leqslant n。

样例:输入 5 2 / aaa 1 / aa 2 / a 3 / ab 3 / bb 4 / b 2 / a 4,输出 ab / Orz YYR tql。

思路

名字结尾字母只有 26 种,读入时按结尾字母分 26 个桶,每个桶内按「评分降序、同分先读入在前」排好序,排序后桶内第 kk 个元素就是该字母下第 kk 大的名字。询问时桶内不足 kk 个就输出 Orz YYR tql,否则按下标直接取名字,单次询问 O(1)O(1)。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 17:40
# update_at: 2026-10-04 09:12

import sys
from collections import defaultdict

ORZ = b"Orz YYR tql"  # 该结尾字母下凑不够第 k 名时的回答


def solve() -> None:
    data = iter(sys.stdin.buffer.read().split())  # token 流:名字与数字都按 next() 顺序消费
    n, m = int(next(data)), int(next(data))

    # 按名字结尾字母分桶:排序键 (-评分, 读入序号),评分大在前、同分先读入在前
    buckets: dict[str, list[tuple[int, int, bytes]]] = defaultdict(list)
    for i in range(2, 2 + 2 * n, 2):
        name = next(data)
        buckets[name[-1:]].append((-int(next(data)), i, name))
    for ranked in buckets.values():
        ranked.sort()

    out: list[bytes] = []
    for j in range(2 * n, 2 * n + 2 * m, 2):
        ranked = buckets.get(next(data))  # 询问的结尾字母
        k = int(next(data))
        out.append(ranked[k - 1][2] if ranked is not None and len(ranked) >= k else ORZ)

    sys.stdout.buffer.write(b"\n".join(out) + b"\n")


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-04 21:38
 * update_at: 2026-10-04 21:38
 */
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;

struct Girl {
    ll score;   // 评分
    ll id;      // 读入编号,从 1 开始
    string name;
};

vector<Girl> bucket[26]; // bucket[c] 存放名字以字符 'a' + c 结尾的妹子
ll cnt[26];             // cnt[c] = bucket[c] 中的元素个数

// 排序比较:评分大的在前,评分相同时先读入(编号小)的在前
bool cmp_girl(const Girl &a, const Girl &b) {
    if (a.score != b.score) return a.score > b.score;
    return a.id < b.id;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    ll n, m;
    cin >> n >> m;

    // 读入时按名字的最后一个字母分桶,顺便记下读入编号
    for (ll i = 1; i <= n; i++) {
        string name;
        ll score;
        cin >> name >> score;
        int c = name[name.size() - 1] - 'a';
        Girl g;
        g.score = score;
        g.id = i;
        g.name = name;
        bucket[c].push_back(g);
        cnt[c]++;
    }

    // 每个桶内部排好序,桶内下标 k - 1 就是该字母下第 k 大的名字
    for (int c = 0; c < 26; c++) {
        sort(bucket[c].begin(), bucket[c].end(), cmp_girl);
    }

    for (ll q = 1; q <= m; q++) {
        char x;
        ll k;
        cin >> x >> k;
        int c = x - 'a';
        if (k > cnt[c]) {
            cout << "Orz YYR tql" << endl;
        } else {
            cout << bucket[c][k - 1].name << endl;
        }
    }

    return 0;
}