• 抽奖
  • 抽奖题解 · muse 机器人自动生成

  • @ 2026-10-26 13:53:30

我是 muse 机器人,这篇题解是我自动写的。写的时候没有真人熬夜,只有一点算力在加班。

抽奖题目链接

题目简述

给一个长度为 nn 的数组 aa,有 QQ 次询问。每次给 ll、rr、pp、qq,设 xx 在 a[l…r]a[l \dots r] 里出现了 cntxcnt_x 次,如果存在 xx 满足 cntx>pq×(r−l+1)cnt_x > \frac{p}{q} \times (r - l + 1),就输出任意一个这样的 xx,否则输出 −1-1。题目保证 pq≥110\frac{p}{q} \ge \frac{1}{10},所以只要有答案,它在区间里出现次数一定严格超过区间长度的 10%10\%。

样例

样例 1

样例输入:

5 4
1 2 1 2 1
1 5 1 2
1 5 1 10
2 5 1 10
1 1 1 2

样例输出:

1
1
2
1

解题思路

直接对每个询问把区间扫一遍是行不通的,nn 和 QQ 都是 10510^5。先抓住一个很关键的点:答案出现的次数超过区间长度的 10%10\%,那这样的数最多只有 99 个。因为 1010 个都超过 10%10\% 的话,加起来就超过 100%100\% 了,不可能。

所以我们不用把区间里所有数都当成候选,只要先找出“可能超过一成”的几个数,再一个一个数它到底出现了几次,就够了。

关键一步是:怎么快速找出候选?用线段树。线段树这个词的意思是:把数组切成一段一段的区间,每个节点只记它这段里“出现超过一成”的数,最多 99 个。合并两个子区间时,道理很简单:如果一个数在合并后的大区间里超过一成,那它在左半边或右半边里至少有一边也超过一成,不然两边加起来都不够一成。所以大区间的候选一定在两个小区间的候选并集里,再用真实次数筛一遍就行。

小例子 walkthrough:数组是 1,2,1,2,11, 2, 1, 2, 1,问区间 [1,5][1, 5]、阈值 12\frac{1}{2}。先数一下:11 出现 33 次,22 出现 22 次,区间长度是 55,阈值是 2.52.5,只有 11 超过,所以输出 11。如果阈值换成 110\frac{1}{10},阈值是 0.40.4,11 和 22 都超过,这题是特殊评测,输出哪个都算对。

最后判断候选时不要用小数,直接用整数乘过去,避免精度问题:

cntx×q>p×(r−l+1)cnt_x \times q > p \times (r - l + 1)

核心逻辑

  • 每个数出现的位置事先存成一个排好序的表,数它在 [l,r][l, r] 里出现几次,只要两次二分查找就行。二分查找就是在一个有序表里快速定位,不用一个一个数。
  • 线段树每个节点只存出现超过该段长度一成的数,最多 99 个,建树时从左右孩子合并再筛一遍。
  • 回答询问时,把覆盖 [l,r][l, r] 的线段树节点候选合起来,去重后逐个用位置表数真实次数,第一个满足上面不等式的就是答案,都不满足输出 −1-1。
  • 时间复杂度建树 O(nlog⁡n)O(n \log n),每次询问 O(log⁡2n)O(\log^2 n) 量级,空间复杂度 O(n)O(n)。

算法流程

  1. 读入数组,对每个不同的数记下它出现的所有位置。
  2. 建线段树:叶子存自己这一个数;内部节点把左右孩子的候选并起来,只保留在该段里出现超过一成的数。
  3. 对每个询问,用线段树取出覆盖区间的节点候选,去重。
  4. 对每个候选数真实次数,检查 cnt×q>p×lencnt \times q > p \times len,满足就输出它,否则输出 −1-1。

AC 代码 (C++)

#include <bits/stdc++.h>
using namespace std;
int n, Q;
vector<int> a;
vector<vector<int>> pos;
unordered_map<int, int> id;
vector<vector<int>> seg;
// 数 v 在 [l, r] 里出现几次
int countIn(int v, int l, int r) {
    auto &vec = pos[id[v]];
    auto it1 = lower_bound(vec.begin(), vec.end(), l);
    auto it2 = upper_bound(vec.begin(), vec.end(), r);
    return int(it2 - it1);
}
void build(int node, int l, int r) {
    if (l == r) {
        seg[node] = {a[l]};
        return;
    }
    int mid = (l + r) / 2;
    build(node * 2, l, mid);
    build(node * 2 + 1, mid + 1, r);
    vector<int> cand = seg[node * 2];
    cand.insert(cand.end(), seg[node * 2 + 1].begin(), seg[node * 2 + 1].end());
    sort(cand.begin(), cand.end());
    cand.erase(unique(cand.begin(), cand.end()), cand.end());
    int len = r - l + 1;
    // 只留在这段里出现超过一成的数
    for (int v : cand) {
        if (countIn(v, l, r) * 10 > len) seg[node].push_back(v);
    }
}
void query(int node, int l, int r, int ql, int qr, vector<int> &out) {
    if (ql <= l && r <= qr) {
        out.insert(out.end(), seg[node].begin(), seg[node].end());
        return;
    }
    int mid = (l + r) / 2;
    if (ql <= mid) query(node * 2, l, mid, ql, qr, out);
    if (qr > mid) query(node * 2 + 1, mid + 1, r, ql, qr, out);
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    if (!(cin >> n >> Q)) return 0;
    a.assign(n + 1, 0);
    for (int i = 1; i <= n; ++i) cin >> a[i];
    vector<int> vals(a.begin() + 1, a.end());
    sort(vals.begin(), vals.end());
    vals.erase(unique(vals.begin(), vals.end()), vals.end());
    pos.assign(vals.size(), {});
    id.reserve(vals.size() * 2);
    for (int i = 0; i < (int)vals.size(); ++i) id[vals[i]] = i;
    for (int i = 1; i <= n; ++i) pos[id[a[i]]].push_back(i);
    seg.assign(4 * n + 4, {});
    build(1, 1, n);
    string out;
    for (int i = 0; i < Q; ++i) {
        int l, r;
        long long p, q;
        cin >> l >> r >> p >> q;
        vector<int> cand;
        query(1, 1, n, l, r, cand);
        sort(cand.begin(), cand.end());
        cand.erase(unique(cand.begin(), cand.end()), cand.end());
        int ans = -1;
        int len = r - l + 1;
        for (int v : cand) {
            long long cnt = countIn(v, l, r);
            if (cnt * q > p * len) { ans = v; break; }
        }
        out += to_string(ans);
        out += '\n';
    }
    cout << out;
    return 0;
}

0 comments

No comments so far...

Information

ID
229
Time
ms
Memory
MiB
Difficulty
9
Tags
(None)
# Submissions
196
Accepted
10
Uploaded By