重构算法大赛 D zeal
给出n个数,q次询问,每次询问求区间(l, r)当中出现k次的数有多少个
0 < a, k <= n <= 4e4, q <= 1e4, l <= r
一个莫队的板子题,通过这道题学到了
对于可以离线的询问,且可以 O ( 1 ) O(1) O(1)时间从ans(l, r)转移到ans(l - 1, r), ans(l + 1, r), ans(l, r + 1)和ans(l, r - 1),就可以用莫队算法
具体的算法流程大致是分块排序,然后暴力区间转移,可以证明复杂度为 O ( n n ) O(n\sqrt{n}) O(nn)
此题当中,用一个cnt数组记录当前出现i次的数有多少个,再用num数组记录i出现的个数,便可以实现 O ( 1 ) O(1) O(1)转移
#include <bits/stdc++.h>
using namespace std;
//faster_reader
inline int read ()
{
int f = 1, a = 0;
char c = getchar ();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar ();
}
while (c >= '0' && c <= '9')
{
a = a * 10 + c - '0';
c = getchar ();
}
return f * a;
}
const int N = 4e4 + 10;
int a[N], belong[N], ans[N];
int num[N], cnt[N];
struct query
{
int l, r, k, num;
};
query q[N];
bool cmp(const query &a, const query &b)
{
if (belong[a.l] ^ belong[b.l])
return belong[a.l] < belong[b.l];
if (belong[a.l] & 1)
return belong[a.r] < belong[b.r];
return belong[a.r] > belong[b.r];
}
void add(int x)
{
cnt[num[a[x]]] --;
num[a[x]] ++;
cnt[num[a[x]]] ++;
}
void sub(int x)
{
cnt[num[a[x]]] --;
num[a[x]] --;
cnt[num[a[x]]] ++;
}
int main()
{
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
//input
int n, m;
n = read(); m = read();
for (int i = 1; i <= n; i ++)
a[i] = read();
for (int i = 1; i <= m; i ++)
{
q[i].l = read();
q[i].r = read();
q[i].k = read();
q[i].num = i;
}
int size = sqrt(n);
for (int i = 1; i <= n; i ++)
belong[i] = (i - 1) / size + 1;
sort (q + 1, q + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; i ++)
{
int ll = q[i].l, rr = q[i].r;
while (l > ll) add(-- l);
while (r < rr) add(++ r);
while (l < ll) sub(l ++);
while (r > rr) sub(r --);
ans[q[i].num] = cnt[q[i].k];
}
for (int i = 1; i <= m; i ++)
printf ("%d\n", ans[i]);
return 0;
}