SPOJ 3267 DQUERY - D-query
作者:互联网
题目大意
对于一个数列 \(\left\{a_n\right\}\) , 有 \(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 \times 10^4\) , \(1 \leq q \leq 2 \times 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\n", 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);
}
标签:10,int,MAX,DQUERY,leq,SPOJ,Query,query,define 来源: https://www.cnblogs.com/kcn999/p/13437508.html