题目大意
对于一个数列 (left{a_n
ight}) , 有 (q) 次询问,第 (i) 次询问格式为 (l_i,r_i) , 求 (a_{l_i},a_{l_i + 1},a_{l_i + 2},dots,a_{r_i}) 中有多少不同的数。
其中, (1 leq n leq 3 imes 10^4) , (1 leq q leq 2 imes 10^5) , (1 leq a_i leq 10^6) 。
题解
建 (n) 棵主席树,第 (i) 棵主席树记录前 (i) 个位置的状态。
在进行 Insert
操作时,如果 (a_i) 在位置 (j) 出现过,那么位置 (i) 赋值为 (1) ,位置 (j) 赋值为 (0) 。
即每次查询的答案为第 (r_i) 棵主席树中 ([l_i,r_i]) 的和。
#include <cstdio>
#include <algorithm>
#define MAX_N (30000 + 5)
#define MAX_A (1000000 + 5)
#define MAX_SIZE 1020000
using std::sort;
struct Tree
{
int size;
int l, r;
};
int n, q;
int a[MAX_N];
int h[MAX_A], nxt[MAX_N];
int root[MAX_N], num;
Tree s[MAX_SIZE];
void Insert(int, int, int, int, int, int);
int Query(int, int, int, int, int);
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
nxt[i] = h[a[i]];
h[a[i]] = i;
}
for (int i = 1; i <= n; ++i)
{
root[i] = ++num;
Insert(root[i - 1], root[i], 1, n, nxt[i], i);
}
scanf("%d", &q);
int l, r;
while (q--)
{
scanf("%d%d", &l, &r);
printf("%d
", Query(root[r], 1, n, l, r));
}
}
void Insert(int r1, int r2, int L, int R, int p1, int p2)
{
if (L == R)
{
if (L == p1) s[r2].size = 0;
else s[r2].size = 1;
return;
}
int mid = L + R >> 1;
s[r2].l = s[r1].l;
s[r2].r = s[r1].r;
if (L <= p1 && p1 <= mid || L <= p2 && p2 <= mid)
{
s[r2].l = ++num;
Insert(s[r1].l, s[r2].l, L, mid, p1, p2);
}
if (mid + 1 <= p1 && p1 <= R || mid + 1 <= p2 && p2 <= R)
{
s[r2].r = ++num;
Insert(s[r1].r, s[r2].r, mid + 1, R, p1, p2);
}
s[r2].size = s[s[r2].l].size + s[s[r2].r].size;
}
int Query(int idx, int L, int R, int lt, int rt)
{
if (R < lt || rt < L) return 0;
if (lt <= L && R <= rt) return s[idx].size;
int mid = L + R >> 1;
return Query(s[idx].l, L, mid, lt, rt) + Query(s[idx].r, mid + 1, R, lt, rt);
}