其他分享
首页 > 其他分享> > SPOJ 3267 DQUERY - D-query

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