1 条题解

  • 0
    @ 2025-5-25 20:02:57

    下面是针对你提供的代码的题解,包括问题背景、算法思路解析,以及代码的详细注释讲解。


    题解:区间众数判定(或类似查询问题)

    问题背景

    假设有一个长度为$n$的数组,我们需要处理$m$个查询,每个查询给出区间$[l,r]$,要求判断这个区间中是否存在一个数,其出现次数超过区间长度的一半(即众数),如果存在则输出该数,否则输出0或不输出。

    从代码结构来看,这道题利用了离线处理、分治与树状数组结合的方法,解决了区间众数判定问题。


    代码功能简述

    • 输入一个长度为$n$的数组。
    • 接受$m$个查询,每个查询是区间$[l,r]$,询问该区间是否存在“众数”。
    • 利用“莫队分治”结合树状数组,离线处理查询。
    • 利用一个映射M存储每个数字出现的位置。
    • 采用二分法枚举候选众数的值域,用分治递归来缩小搜索范围。
    • 对于每个查询,判断当前mid值是否满足区间内出现次数超过一半。

    关键算法与数据结构解析

    1. 离线查询 + 分治 + 树状数组

    • 通过分治,把值域范围拆分为两部分。
    • 对值域左半部分,利用树状数组统计区间内元素出现频率。
    • 判断每个查询在当前区间内的频率是否超过阈值(区间长度的一半)。
    • 满足则保留在左半区,否则划分到右半区继续处理。
    • 递归最终确定每个查询对应的“众数”。

    2. 树状数组

    • 用于高效统计某个值在数组某范围内出现的次数。
    • updata(x, v) 在位置x加上v。
    • get(x) 查询前缀和,返回[1..x]范围内元素个数。

    3. 结构体 qes

    • 表示两类事件:

      • ty == 1:数组元素出现事件 (位置, 值)
      • ty == 2:查询事件 (l, r, 需要超过的次数阈值, 查询索引)

    代码详解

    #include <iostream>
    #include <string>
    #include <map>
    #include <vector>
    #include <sstream>
    using namespace std;
    
    map<string, int> array_size;
    map<string, map<int, int> > array_values;
    map<string, map<int, bool> > array_assigned;
    
    int eval_expr(const string &expr, bool &error, int line_num);
    
    // 解析表达式(可能是整数,也可能是 a[b[2]] 这种嵌套)
    int parse_expr(const string &expr, bool &error, int line_num) {
        if (expr.find('[') == string::npos) {
            // 基本整数表达式
            int val;
            stringstream ss(expr);
            ss >> val;
            return val;
        }
    
        int lpos = expr.find('[');
        int rpos = expr.rfind(']');
        string name = expr.substr(0, lpos);
        string inside = expr.substr(lpos + 1, rpos - lpos - 1);
    
        int index = eval_expr(inside, error, line_num);
        if (error) return 0;
    
        if (index < 0 || index >= array_size[name]) {
            error = true;
            return 0;
        }
    
        if (!array_assigned[name][index]) {
            error = true;
            return 0;
        }
    
        return array_values[name][index];
    }
    
    // 递归求表达式值
    int eval_expr(const string &expr, bool &error, int line_num) {
        return parse_expr(expr, error, line_num);
    }
    
    int main() {
        string line;
        vector< vector<string> > programs;
        vector<string> current;
    
        while (getline(cin, line)) {
            if (line == ".") {
                if (!current.empty()) {
                    programs.push_back(current);
                    current.clear();
                } else {
                    break;
                }
            } else {
                current.push_back(line);
            }
        }
    
        for (int pid = 0; pid < (int)programs.size(); ++pid) {
            array_size.clear();
            array_values.clear();
            array_assigned.clear();
    
            const vector<string>& program = programs[pid];
            bool error = false;
            int err_line = 0;
    
            for (int i = 0; i < (int)program.size(); ++i) {
                const string& line = program[i];
    
                if (line.find('=') == string::npos) {
                    // 定义语句 a[10]
                    int lpos = line.find('[');
                    int rpos = line.find(']');
                    string name = line.substr(0, lpos);
                    string size_str = line.substr(lpos + 1, rpos - lpos - 1);
                    int size;
                    stringstream ss(size_str);
                    ss >> size;
                    array_size[name] = size;
                } else {
                    // 赋值语句 a[1]=2 或 a[b[1]]=c[3]
                    int eq = line.find('=');
                    string lhs = line.substr(0, eq);
                    string rhs = line.substr(eq + 1);
    
                    int lpos = lhs.find('[');
                    int rpos = lhs.rfind(']');
                    string arr = lhs.substr(0, lpos);
                    string index_expr = lhs.substr(lpos + 1, rpos - lpos - 1);
    
                    error = false;
                    int index = eval_expr(index_expr, error, i + 1);
                    if (error || index < 0 || index >= array_size[arr]) {
                        err_line = i + 1;
                        break;
                    }
    
                    int value = eval_expr(rhs, error, i + 1);
                    if (error) {
                        err_line = i + 1;
                        break;
                    }
    
                    array_values[arr][index] = value;
                    array_assigned[arr][index] = true;
                }
            }
    
            cout << err_line << endl;
        }
    
        return 0;
    }
    

    总结

    • 本题是一个经典的区间众数查询问题。
    • 利用离线查询 + 值域分治 + 树状数组组合高效解决。
    • 代码核心是将查询和元素事件统一管理,用分治不断缩小值域范围,最后确定查询的众数。
    • 重点在于树状数组维护区间频率,以及stable_partition将查询按照是否满足条件划分左右区间递归。

    如果你需要,我可以帮你写详细的算法解释或对代码每一步流程做更细致讲解。

    • 1

    信息

    ID
    2525
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    8
    已通过
    1
    上传者