题目链接: P4396 [AHOI2013]作业
大致题意
给定一个长度为
n
n
n的序列, 有
m
m
m次询问, 每次询问给出l r a b
.
表示询问在区间 [ l , r ] [l, r] [l,r]内, 值域 [ a , b ] [a, b] [a,b]之间有多少个数值, 以及有多少个不同数值.
解题思路
莫队 + 值域分块
我们通过莫队做到 O ( n ) O(\sqrt{n}) O(n)维护 [ l , r ] [l, r] [l,r]的所有数值信息, 在查询时, 通过分块来做到 O ( n ) O(\sqrt{n}) O(n)的查询.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, B = 320;
int w[N];
struct mo {
int l, r, a, b, id;
bool operator< (const mo& t) const {
if (l / B != t.l / B) return l < t.l;
return l / B & 1 ? r < t.r : r > t.r;
}
}; vector<mo> area;
int num[N], bcou[B], bhave[B];
pair<int, int> res[N];
void add(int c) {
num[c]++, bcou[c / B]++;
if (num[c] == 1) bhave[c / B]++;
}
void sub(int c) {
num[c]--, bcou[c / B]--;
if (!num[c]) bhave[c / B]--;
}
pair<int, int> ask(int a, int b) {
int cou1 = 0, cou2 = 0;
if (a / B == b / B) { // 同块
while (a <= b) cou1 += num[a], cou2 += num[a++] != 0;
return { cou1, cou2 };
}
int ba = a / B, bb = b / B;
while (a / B == ba) cou1 += num[a], cou2 += num[a++] != 0;
while (b / B == bb) cou1 += num[b], cou2 += num[b--] != 0;
for (int i = a / B; i <= b / B; ++i) {
cou1 += bcou[i], cou2 += bhave[i];
}
return { cou1, cou2 };
}
int main()
{
int n, m; cin >> n >> m;
rep(i, n) scanf("%d", &w[i]);
rep(i, m) {
int l, r, a, b;
scanf("%d %d %d %d", &l, &r, &a, &b);
area.push_back({ l, r, a, b, i });
}
sort(area.begin(), area.end());
int L = 1, R = 0;
for (auto& [l, r, a, b, id] : area) {
while (l < L) add(w[--L]);
while (r > R) add(w[++R]);
while (L < l) sub(w[L++]);
while (R > r) sub(w[R--]);
res[id] = ask(a, b);
}
rep(i, m) printf("%d %d\n", res[i].first, res[i].second);
return 0;
}