トピックリンク:zoto
一般的なアイデア
nnの長さが与えられたnのシーケンス、mmmクエリ、各クエリが与えられl a r b
ます。
クエリが間隔[l、r] [l、r]にあることを示します[ l 、r ]、範囲[a、b] [a、b][ a 、b ]いくつの異なる値があります。
元の説明とは少し異なりますが、本質はこれです。
問題解決のアイデア
Moチーム+レンジブロック
区間[l、r] [l、r]を取得することを検討します[ l 、r ]情報、O(n)O(\ sqrt {n})(_n。)変更、O(1)O(1)O (1 )クエリ。
この質問では、特定の範囲の値にいくつの異なる値があるかを数える必要があります。もう少し暴力的でツリー配列のメンテナンスを使用している場合は、O(nlogn)O(\ sqrt {n} logn)を実行できます。(_n。l o g n )変更、O(1)O(1)O (1 )クエリ。
ただし、このように、変更の複雑さが高すぎるため、クエリの変更の複雑さを償却する。
値の範囲をブロックに分割することで、各範囲ブロックの要素のタイプを記録できます。これは、O(n)O(\ sqrt {n})で実行できます。(_n。)、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 res[N];
int cou[N], bcou[B];
void add(int c) {
if (++cou[c] == 1) bcou[c / B]++; }
void sub(int c) {
if (!--cou[c]) bcou[c / B]--; }
int ask(int a, int b) {
int res = 0;
if (a / B == b / B) {
while (a <= b) res += cou[a++] != 0;
return res;
}
int la = a / B, lb = b / B;
while (a / B == la) res += cou[a++] != 0;
while (b / B == lb) res += cou[b--] != 0;
for (int i = a / B; i <= b / B; ++i) res += bcou[i];
return res;
}
int main()
{
int T; cin >> T;
while (T--) {
area.clear();
int n, m; scanf("%d %d", &n, &m);
rep(i, n) scanf("%d", &w[i]);
rep(i, m) {
int l, r, a, b; scanf("%d %d %d %d", &l, &a, &r, &b);
area.push_back({
l, r, a, b, i });
}
sort(area.begin(), area.end());
int L = 1, R = 0;
for (auto& op : area) {
int l = op.l, r = op.r, a = op.a, b = op.b, id = op.id;
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);
}
while (L <= R) sub(w[L++]); // 清空信息
rep(i, m) printf("%d\n", res[i]);
}
return 0;
}