P4168 [Violet]蒲公英
题意
给 个数, 询问区间众数, 询问 次.
, ,
分析
区间查询一般会想到线段树, 但是线段树不能维护众数这种不能合并的信息.
朴素
到 扫一遍, 统计数字出现数量, 然后扫一遍计数器.
, 约为
离散化
考虑离散化, 最多有 个不同的数, 预处理复杂度 .
优化成 $O(mn^2) =
前缀和
用一个二维数组 记录前 个数中, 出现几次.
空间复杂度 . 预处理复杂度 , 时间复杂度 .
分块
整体维护, 局部朴素
将 个数分成长度为 的 块, 然后以块为单位存前缀和, 同时预处理出 表示从第 块到第 块 (闭区间) 的众数.
在查询时, 对于区间两端不足一块的, 朴素计算出现在这两区域内存在的数字的出现次数, 然后根据前缀和算出整个区间内这些数字的出现次数, 然后比较其中出现最多的数字出现次数和掐头去尾后整块的众数的出现次数, 因为区间众数不是在两头出现过, 就是整块区的众数.
这种做法, 和 数组预处理复杂度 , 总复杂度优化到了 .
为了使复杂度最小, 选取 时使得 , 由于本题 , 较接近, 所以姑且取 .
实现
详见注释, 快读 RD()
, 头文件, 变量定义等略.
int main() {
n = RD();
m = RD();
memset(Ap, 0, sizeof(Ap));
Rg = max(int(sqrt(n)), 1); //确定Rg
NmR = (n + Rg - 1) / Rg; //推得NmR
for (register int i(1); i <= n; ++i) {
a[i] = RD();
b[i] = a[i]; //创建 a[]副本
}
sort(b + 1, b + n + 1);
for (register int i(1); i <= n; ++i) {
if (b[i] != b[i - 1]) {
Ar[++Cnta] = b[i]; // Ar 存严格次序中第 k 小的数
}
}
for (register int i(1); i <= n; ++i) { //离散化
a[i] = lower_bound(Ar + 1, Ar + Cnta + 1, a[i]) -
Ar; //将每个a[i]变成小于等于n的数
}
for (register int i(1); i < NmR; ++i) { //处理Ap[][]
for (register int j(Rg * (i - 1) + 1); j <= Rg * i; ++j) {
++Ap[i][a[j]];
}
for (register int j(1); j <= Cnta; ++j) { //继承给下一块
Ap[i + 1][j] = Ap[i][j];
}
}
//最后一行
for (register int i(Rg * (NmR - 1) + 1); i <= n; ++i) { //最后一行特殊处理
++Ap[NmR][a[i]];
}
for (register int i(1); i < NmR; ++i) { //处理长度为 1 块的区间的 f[][]
Tmp = 0;
for (register int j(Rg * (i - 1) + 1); j <= Rg * i;
++j) { //枚举每一个出现过的数字
if (Ap[i][Tmp] - Ap[i - 1][Tmp] <= Ap[i][a[j]] - Ap[i - 1][a[j]]) {
if (Ap[i][Tmp] - Ap[i - 1][Tmp] == Ap[i][a[j]] - Ap[i - 1][a[j]]) {
if (Tmp > a[j]) {
Tmp = a[j];
}
} else {
Tmp = a[j];
}
}
}
f[i][i] = Tmp;
}
Tmp = 0;
for (register int i(Rg * (NmR - 1) + 1); i <= n; ++i) { //最后一行特殊处理
if (Ap[NmR][Tmp] - Ap[NmR - 1][Tmp] <= Ap[NmR][a[i]] - Ap[NmR - 1][a[i]]) {
if (Ap[NmR][Tmp] - Ap[NmR - 1][Tmp] ==
Ap[NmR][a[i]] - Ap[NmR - 1][a[i]]) {
if (Tmp > a[i]) {
Tmp = a[i];
}
} else {
Tmp = a[i];
}
}
f[NmR][NmR] = Tmp;
}
for (register int i(1); i <= NmR; ++i) {
for (register int j(i + 1); j <= NmR; ++j) { //处理全部f[][]
if (f[i][j - 1] == f[j][j]) { //共同众数无需处理
f[i][j] = f[j][j];
} else {
Tmp = f[i][j - 1];
for (register int k(Rg * (j - 1) + 1); k <= min(Rg * j, n);
++k) { //枚举出现过的数字
if (Ap[j][Tmp] - Ap[i - 1][Tmp] <= Ap[j][a[k]] - Ap[i - 1][a[k]]) {
if (Ap[j][Tmp] - Ap[i - 1][Tmp] == Ap[j][a[k]] - Ap[i - 1][a[k]]) {
if (Tmp > a[k]) { //数字小的优先
Tmp = a[k];
}
} else {
Tmp = a[k]; //更新众数
}
}
}
f[i][j] = Tmp; //众数以确定
}
}
}
for (register int i(1); i <= m; ++i) { //处理询问
L = (RD() + Lst - 1) % n + 1;
R = (RD() + Lst - 1) % n + 1; //区间生成(强制在线)
if (L > R) { //判左大右小
swap(L, R);
}
Lr = (L + Rg - 1) / Rg + 1;
Rr = R / Rg; //处理包含的最左块和最右块
if (Lr > Rr) { //整块不存在
for (register int j(L); j <= R; ++j) { //直接朴素
Tmpp[a[j]] = 0; //清空计数器(下同)
}
for (register int j(L); j <= R; ++j) {
++Tmpp[a[j]];
}
Tmp = 0;
for (register int j(L); j <= R; ++j) {
if (Tmpp[Tmp] <= Tmpp[a[j]]) {
if (Tmpp[Tmp] == Tmpp[a[j]]) {
if (Tmp > a[j]) {
Tmp = a[j];
}
} else {
Tmp = a[j];
}
}
}
Lst = Tmp;
} else { //有整块
Tmp = f[Lr][Rr]; //先和判整块众数出现次数比较
Tmpp[Tmp] = 0; //别忘了这里
for (register int j(L); j <= Rg * (Lr - 1); ++j) { //掐头
Tmpp[a[j]] = 0;
}
for (register int j(Rg * Rr + 1); j <= R; ++j) { //去尾
Tmpp[a[j]] = 0;
}
for (register int j(L); j <= Rg * (Lr - 1); ++j) {
++Tmpp[a[j]];
}
for (register int j(Rg * Rr + 1); j <= R; ++j) {
++Tmpp[a[j]];
}
for (register int j(L); j <= Rg * (Lr - 1); ++j) { //开始迭代
if (Tmpp[Tmp] + Ap[Rr][Tmp] - Ap[Lr - 1][Tmp] <=
Tmpp[a[j]] + Ap[Rr][a[j]] -
Ap[Lr - 1][a[j]]) { //当前数字出现次数和当前已知众数出现次数
if (Tmpp[Tmp] + Ap[Rr][Tmp] - Ap[Lr - 1][Tmp] ==
Tmpp[a[j]] + Ap[Rr][a[j]] - Ap[Lr - 1][a[j]]) {
if (Tmp > a[j]) {
Tmp = a[j];
}
} else {
Tmp = a[j];
}
}
}
for (register int j(Rg * Rr + 1); j <= R; ++j) { //尾操作同头
if (Tmpp[Tmp] + Ap[Rr][Tmp] - Ap[Lr - 1][Tmp] <=
Tmpp[a[j]] + Ap[Rr][a[j]] - Ap[Lr - 1][a[j]]) {
if (Tmpp[Tmp] + Ap[Rr][Tmp] - Ap[Lr - 1][Tmp] ==
Tmpp[a[j]] + Ap[Rr][a[j]] - Ap[Lr - 1][a[j]]) {
if (Tmp > a[j]) {
Tmp = a[j];
}
} else {
Tmp = a[j];
}
}
}
Lst = Tmp;
}
Lst = Ar[Lst]; //离散化后的值转化为原始值
printf("%d\n", Lst);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具