区间众数
金牌导航 分块-1
题目大意
给出一个数列,和若干询问,每个询问让你求一个区间内的众数
输入样例
6 3
1 2 3 2 1 2
1 5
3 6
1 5
输出样例
1
2
1
数据范围
1⩽N⩽4×104,1⩽M⩽5×104,1⩽ai⩽1091\leqslant N \leqslant 4\times 10^4,1\leqslant M \leqslant 5\times 10^4,1\leqslant a_i \leqslant 10^91⩽N⩽4×104,1⩽M⩽5×104,1⩽ai⩽109
解题思路
对于求区间众数,线段树不易维护
考虑分块
先对数列a离散化
然后枚举一遍数列a,求出numi,jnum_{i,j}numi,j(前i个块中j颜色出现的次数),时间O(nn)O(n\sqrt{n})O(nn)
然后求一段连续块的众数,先枚举左段的块,然后暴力搜右节点,时间O(nn)O(n\sqrt{n})O(nn)
然后对于每次查询,暴力枚举不在整一个块的数
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 40010
#define QN 210
using namespace std;
int n, m, x, y, g, L, R, nn, nm, a[N], b[N], p[N], f[QN][QN], num[QN][N];
int main()
{scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i){scanf("%d", &a[i]);b[i] = a[i];}sort(b + 1, b + 1 + n);g = unique(b + 1, b + 1 + n) - b - 1;for (int i = 1; i <= n; ++i)a[i] = lower_bound(b + 1, b + 1 + g, a[i]) - b;nn = sqrt(n);g = 0;nm = n / nn + (n % nn? 1: 0);for (int i = 1; i <= n; ++i){p[a[i]]++;if (i % nn == 0){g++;for (int j = 1; j <= n; ++j)num[g][j] = p[j];}}if (g < nm){for (int j = 1; j <= n; ++j)num[n / nn + 1][j] = p[j];}for (int i = 1; i <= nm; ++i){g = 0;memset(p, 0, sizeof(p));for (int j = (i - 1) * nn + 1; j <= n; ++j){p[a[j]]++;if (p[a[j]] > p[g] || p[a[j]] == p[g] && a[j] < g) g = a[j];if (j % nn == 0) f[i][j / nn] = g;}}memset(p, 0, sizeof(p));g = 0;while(m--){scanf("%d%d", &x, &y);x = (x + b[g] - 1) % n + 1;y = (y + b[g] - 1) % n + 1;if (x > y) swap(x, y);L = (x + nn - 2) / nn + 1;R = y / nn;if (L > R)//没包含任意一个整的块直接暴力{g = 0;for (int i = x; i <= y; ++i){p[a[i]]++;if (p[a[i]] > p[g] || p[a[i]] == p[g] && a[i] < g)g = a[i];}for (int i = x; i <= y; ++i)p[a[i]]--;printf("%d\n", b[g]);continue;}g = f[L][R];for (int i = x; i <= min((L - 1) * nn, n); ++i){p[a[i]]++;int X = p[a[i]] + num[R][a[i]] - num[L - 1][a[i]];//加上整的块里面的int Y = p[g] + num[R][g] - num[L - 1][g];if (X > Y || X == Y && a[i] < g)g = a[i];}for (int i = max(R * nn + 1, 1); i <= y; ++i){p[a[i]]++;int X = p[a[i]] + num[R][a[i]] - num[L - 1][a[i]];int Y = p[g] + num[R][g] - num[L - 1][g];if (X > Y || X == Y && a[i] < g)g = a[i];}for (int i = x; i <= min((L - 1) * nn, n); ++i)//把原来的清掉,不然要O(n),会超时p[a[i]]--;for (int i = max(R * nn + 1, 1); i <= y; ++i)p[a[i]]--;printf("%d\n", b[g]);}return 0;
}