トピックリンク:P4396[AHOI2013]割り当て
一般的なアイデア
nnの長さが与えられたnのシーケンス、mmmクエリ、各クエリが与えられl r a b
ます。
クエリが間隔[l、r] [l、r]にあることを示します[ l 、r ]、範囲[a、b] [a、b][ a 、b ]、いくつの値があり、いくつの異なる値があります。
問題解決のアイデア
Moチーム+レンジブロック
MoチームによってO(n)O(\ sqrt {n})を実行します(_n。) [l、r] [l、r]を維持し[ l 、r ]のすべての数値情報は、クエリを実行するときに、 O(n)O(\ sqrt {n})をブロックすることによって実行され(_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;
}