1.题目链接。给定一个数组,然后m个询问,每组询问[l,r]输出[l,r]这个区间有多少种元素。
2.这个题目的写法很多,可以莫队,可以树状数组/线段树,也可以直接在线主席树。在这里面感觉离线后树状数组是最好写的(当然,主席树我也写不好)。首先是需要离线所有的询问,然后按照区间的右端点排序,然后对于每一个询问tree[j]存的是[1,j]里面所有元素的的种类,然后对于每一组询问,我们从l,遍历到r,看一下这个区间里第i个位置的数字是不是在前边出现过?如果出现过,就把他从前边出现的位置删了。然后把这个地方的值插进树状数组,注意这里的插入树状数组,不是在这个地方插值,而是插入位置,说明这里出现了一个新的数。然后前缀和就是我们想要的答案。
#include<bits/stdc++.h>
#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;
}
来源:CSDN
作者:MatrixYg
链接:https://blog.csdn.net/weixin_41863129/article/details/89819841