【ybt金牌导航6-3-1】【luogu P4168】区间众数 / 蒲公英 / 分块例题

区间众数 / 蒲公英

题目链接:ybt金牌导航6-3-1 / luogu P4168

题目大意

给出一个序列,每次求一个区间的众数。
众数:出现次数最多的数,如果有多个数字,则选小的那个。
强制在线。

思路

这道题是分块的例题。

分块就是把数列分成很多个块,然后这些块是可以整个算的。
这样的话,如果每次你要询问区间,你可以把它分成三个部分:前面多出来的,后面多出来的,中间若干个区间。
前面后面多出来的我们可以直接暴力,然后中间就可以弄个前缀和或者什么的快速地求出来。

然后我们想想分块,每个块的大小是多少最优。
你既要顾忌块的个数,还要顾忌块的大小,那我们就让这两个尽可能相同,那就是 n 的大小,分成大概 n 段。
(是大概是因为最后可以会多出来一点,但是这一点无所谓,算就是了)

那这道题也是这样子,但是你看到数字很大,然后决定要记录每个数字在区间中出现的次数。
那就把数字离散化一下,然后就按着上面的方法做。

然后具体可以看一下代码。

代码

#include<cmath> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct node { int x, num, val; }a[40001]; int n, m, dy[40001], tot, K; int num[201][40001], x, y, ans; int sum[40001], maxn, maxx; bool cmp(node x, node y) { return x.x < y.x; } bool cmp_b(node x, node y) { return x.num < y.num; } int work(int x, int y) { maxn = -1; memset(sum, 0, sizeof(sum)); while (x % K != 0 && x <= y) {//处理前面的多出来的 sum[a[x].val]++; if (sum[a[x].val] > maxn) { maxn = sum[a[x].val]; maxx = a[x].val; } else if (sum[a[x].val] == maxn && a[x].val < maxx) { maxx = a[x].val; } x++; } while (y % K != K - 1 && x <= y) {//处理后面多出来的 sum[a[y].val]++; if (sum[a[y].val] > maxn) { maxn = sum[a[y].val]; maxx = a[y].val; } else if (sum[a[y].val] == maxn && a[y].val < maxx) { maxx = a[y].val; } y--; } if (x > y) {//没有中间的大块 return dy[maxx]; } for (register int i = 0; i <= tot; i++) {//枚举数 sum[i] += num[y / K][i] - ((x / K - 1 < 0) ? 0 : num[x / K - 1][i]); //利用前缀和算出大区间中有多少个这个数 if (sum[i] > maxn) { maxn = sum[i]; maxx = i; } else if (sum[i] == maxn && i < maxx) { maxx = i; } } return dy[maxx]; } int read() { int re = 0; char c = getchar(); while (c < '0' || c > '9') c = getchar(); while (c >= '0' && c <= '9') { re = re * 10 + c - '0'; c = getchar(); } return re; } void write(int now) { if (now > 9) write(now / 10); putchar(now % 10 + '0'); } int main() { n = read(); m = read(); for (register int i = 0; i < n; i++) { a[i].x = read(); a[i].num = i; } sort(a, a + n, cmp);//离散化 a[0].val = 0; dy[tot] = a[0].x; for (register int i = 1; i < n; i++) { if (a[i].x != a[i - 1].x) { tot++; dy[tot] = a[i].x; } a[i].val = tot; } sort(a, a + n, cmp_b); K = floor(sqrt(n));//分块 for (register int i = 0; i < n; i++) {//算每个块每个数字的数量 num[i / K][a[i].val]++; } for (register int i = 1; i < n / K; i++)//前缀和算1~i个块中数字j的数量有多少个 for (register int j = 0; j <= tot; j++) num[i][j] += num[i - 1][j]; for (register int i = 1; i <= m; i++) { x = read(); y = read(); x = ((x + ans - 1) % n) + 1; y = ((y + ans - 1) % n) + 1; if (x > y) swap(x, y);//解码 x--;//因为我是从0编号,所以编号要减一 y--; ans = work(x, y); write(ans); putchar('\n'); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_6-3-1.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(46)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示