众数

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

区间查询时统计每个数出现次数,维护出现次数最大且数值最小的那个数;修改直接单点赋值。

OJ: luogu

题目 ID: P2681

难度:入门

标签:模拟枚举

日期: 2026-06-19 01:11

题意

给定一个序列,支持两种操作:

  • 0 x y:查询区间 [x, y] 的众数
  • 1 x y:把 a[x] 修改成 y

如果区间里有多个众数,输出其中较小的那个。

思路

这题的数据范围很小:n, m <= 1000

所以查询时没有必要上复杂数据结构,直接统计区间出现次数就够了。

先看一个更直接的朴素做法:

对区间里的每个数,再扫一遍区间,数它一共出现了几次,然后取出现次数最大、数值最小的那个。

这个版本最容易理解,也方便对拍:

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

const int MAXN = 1005;

int n, m;
int a[MAXN];

int query_mode(int l, int r) {
    int best_value = 0;
    int best_count = -1;

    // 枚举区间里的每一个数,重新数它出现了多少次。
    for (int i = l; i <= r; i++) {
        int cur_value = a[i];
        int cur_count = 0;

        for (int j = l; j <= r; j++) {
            if (a[j] == cur_value) {
                cur_count++;
            }
        }

        if (cur_count > best_count ||
            (cur_count == best_count && cur_value < best_value)) {
            best_count = cur_count;
            best_value = cur_value;
        }
    }

    return best_value;
}

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

    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    for (int i = 1; i <= m; i++) {
        int flag, x, y;
        cin >> flag >> x >> y;

        if (flag == 0) {
            cout << query_mode(x, y) << '\n';
        }
        else {
            a[x] = y;
        }
    }

    return 0;
}

正式做法可以稍微省一点重复工作:

查询 [l, r] 时,从左到右扫一遍区间,用 map 统计每个数出现了多少次。

每当某个数的计数加一时,就检查它是否能更新当前答案:

  • 出现次数更多,一定更优
  • 出现次数相同,则数值更小更优

修改操作就直接做单点赋值。

代码

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

const int MAXN = 1005;

int n, m;
int a[MAXN];

int query_mode(int l, int r) {
    map<int, int> cnt;
    int best_value = 0;
    int best_count = 0;

    for (int i = l; i <= r; i++) {
        cnt[a[i]]++;
        if (cnt[a[i]] > best_count ||
            (cnt[a[i]] == best_count && a[i] < best_value)) {
            best_count = cnt[a[i]];
            best_value = a[i];
        }
    }

    return best_value;
}

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

    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    for (int i = 1; i <= m; i++) {
        int flag, x, y;
        cin >> flag >> x >> y;

        if (flag == 0) {
            cout << query_mode(x, y) << '\n';
        }
        else {
            a[x] = y;
        }
    }

    return 0;
}

复杂度

设一次查询区间长度为 len

  • 查询复杂度是 O(lenloglen)O(len log len)
  • 修改复杂度是 O(1)O(1)

在本题数据范围下完全够用。

总结

这题的重点是看到数据范围很小,不要过度设计。

按题意直接统计频率,就是最稳的做法。