Step1 Problem:
给你 n 个数和 q 个询问,每次询问 问你 [L, R] 不同数的个数。
Step2 Ideas:
主席树:
1:每一颗线段树区间维护的是不同数的个数。
2:每一颗线段树维护的值,是最后出现的。例如第 4 棵线段树,维护时只要第 4 个 1,前两个 1 不要。
对于询问 [L, R] 我们只需要第 R 棵线段树,直接求 [L, R] 范围内不同数的个数。
对于第 2 点如何解决,如果第 i 个数,在前面已经出现过,那么我们先删除第 i 个数,然后在插入。
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
#define MID int mid = (l+r)/2
const int N = 3e4+5;
struct node
{
int sum, l, r;
}tree[N*80];
int vis[1000005], a[N], root[N], top = 0;
int creat(int sum, int l, int r)
{
tree[top++] = (node){sum, l, r};
return top-1;
}
void updata(int &root, int pre, int l, int r, int pos, int v)
{
root = creat(tree[pre].sum+v, tree[pre].l, tree[pre].r);
if(l == r) return ;
MID;
if(pos <= mid) updata(tree[root].l, tree[pre].l, l, mid, pos, v);
else updata(tree[root].r, tree[pre].r, mid+1, r, pos, v);
}
int query(int root, int l, int r, int k)//区间大于 k 我们才要。
{
if(l == r) return tree[root].sum;
MID;
int ret = 0;
if(k <= mid) ret += tree[tree[root].r].sum + query(tree[root].l, l, mid, k);
else ret += query(tree[root].r, mid+1, r, k);
return ret;
}
int main()
{
int n, m;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", a+i);
scanf("%d", &m);
root[0] = creat(0, 0, 0);
memset(vis, 0, sizeof(vis));
int tmp;
for(int i = 1; i <= n; i++)
{
if(!vis[a[i]]) {//前面没出现过,直接插入
updata(root[i], root[i-1], 1, n, i, 1);
}
else {//如果前面出现过,先删除,在插入
updata(tmp, root[i-1], 1, n, vis[a[i]], -1);
updata(root[i], tmp, 1, n, i, 1);
}
vis[a[i]] = i;
}
int ul, ur;
while(m--)
{
scanf("%d %d", &ul, &ur);
printf("%d\n", query(root[ur], 1, n, ul));//直接求[ul, ur]范围内不同数的个数
}
return 0;
}