1 条题解
-
0
下面是针对你提供的代码的题解,包括问题背景、算法思路解析,以及代码的详细注释讲解。
题解:区间众数判定(或类似查询问题)
问题背景
假设有一个长度为$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
- 上传者