【LUOGU 1972】HH的项链

1.题目链接。给定一个数组,然后m个询问,每组询问[l,r]输出[l,r]这个区间有多少种元素。

2.这个题目的写法很多,可以莫队,可以树状数组/线段树,也可以直接在线主席树。在这里面感觉离线后树状数组是最好写的(当然,主席树我也写不好)。首先是需要离线所有的询问,然后按照区间的右端点排序,然后对于每一个询问tree[j]存的是[1,j]里面所有元素的的种类,然后对于每一组询问,我们从l,遍历到r,看一下这个区间里第i个位置的数字是不是在前边出现过?如果出现过,就把他从前边出现的位置删了。然后把这个地方的值插进树状数组,注意这里的插入树状数组,不是在这个地方插值,而是插入位置,说明这里出现了一个新的数。然后前缀和就是我们想要的答案。

#include
#pragma warning(disable:4996)
using namespace std;
const int maxn = 1e6 + 100;
int num[maxn], tree[maxn], vis[maxn], ans[maxn], N, Q;
struct point
{
	int l, r;
	int pos;
};
point ask[maxn];
bool cmp(point x, point y)
{
	return x.r < y.r;
}
void add(int n, int now)
{
	while (n <= N)
	{
		tree[n] += now;
		n +=(n&(-n));
	}
}
int query(int n)
{
	int ans = 0;
	while (n != 0)
	{
		ans += tree[n];
		n -=(n&(-n));
	}
	return ans;
}
int main()
{
	scanf("%d", &N);
	for (int i = 1; i <= N; i++)
		scanf("%d", &num[i]);
	scanf("%d", &Q);
	for (int i = 1; i <= Q; i++)
	{
		scanf("%d%d", &ask[i].l, &ask[i].r);
		ask[i].pos = i; 
	}
	sort(ask + 1, ask + 1 + Q, cmp);
	int next = 1;
	for (int i = 1; i <= Q; i++)
	{
		for (int j = next; j <= ask[i].r; j++)
		{
			if (vis[num[j]])
				add(vis[num[j]], -1);
			add(j, 1);
			vis[num[j]] = j;
		}
		next = ask[i].r + 1;
		ans[ask[i].pos] = query(ask[i].r) - query(ask[i].l - 1);
	}
	for (int i = 1; i <= Q; i++)
		cout << ans[i] << endl;
	return  0;
}

 

你可能感兴趣的:(数据结构与算法基于c++实现)