莫队求区间众数
假设有 (n) 个数,莫队的复杂度是 (n*sqrt(n))
问题:给你一个 (n) 的序列,有 (m) 次询问,每次询问一个区间 ([l,r]) 出现频率最高的数的次数是多少?
复杂度: (n*sqrt(n))
例题:D. Cut and Stick
题目大意:
给你一个序列 (a),保证: (a_ileq n) ,每次询问一个区间 ([l,r]) ,你可以把这个区间的数分成 (x) 个集合,设 (siz) = 这个集合的大小,要求任意一个集合内相同数的重复次数小于等于 (left lceil frac{siz}{2} ight ceil) ,问最小的 (x) 是多少?
题解:
因为题目难点在于如何求区间出现频率最高的数的次数,所以本篇博客只解决这个问题。
之前写过一篇博客 D. Cut and Stick 线段树 是用线段树的方法解决的,但是这个办法只能求频率大于等于 (left lceil frac{siz}{2} ight ceil) 的次数,是有一个先决条件的。
接下来讲的方法是没有任何条件的,只需要复杂度满足即可。
维护众数的出现次数:用 (cnt[i]) 维护数字i的出现次数,用 (num[i]) 维护出现了 (i) 次的数字有多少个,因此,最大的不为 0 的 (num[]) 的下标即为众数的出现次数。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
typedef long long ll;
int a[maxn], pos[maxn] ,vis[maxn],ans[maxn];
struct node{
int l, r, id;
}ex[maxn];
bool cmp(node a,node b){
return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
}
/*
* 维护众数的出现次数:用cnt[i]维护数字i的出现次数,用num[i]维护出现了i次的数字有多少个,因此,最大的不为0的num[]的下标即为众数的出现次数。
*/
int cnt[maxn],num[maxn],Ans;
void add(int x) {
--num[cnt[a[x]]], ++num[++cnt[a[x]]];
while (num[Ans + 1]) ++Ans;
}
void del(int x) {
--num[cnt[a[x]]],++num[--cnt[a[x]]];
while (!num[Ans]) --Ans;
}
int main() {
int n, q;
scanf("%d%d", &n, &q);
int siz = sqrt(n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
pos[i] = i / siz;
}
for (int i = 1; i <= q; i++) {
scanf("%d%d", &ex[i].l, &ex[i].r);
ex[i].id = i;
}
sort(ex + 1, ex + 1 + q, cmp);
Ans = 0,num[0] = 1;
int l = 1, r = 0;
for (int i = 1; i <= q; i++) {
int ql = ex[i].l, qr = ex[i].r;
while (r < qr) add(++r);
while (r > qr) del(r--);
while (l < ql) del(l++);
while (l > ql) add(--l);
ans[ex[i].id] = 2 * Ans - (qr - ql + 1);
}
for (int i = 1; i <= q; i++) {
printf("%d
", max(1, ans[i]));
}
return 0;
}