\
\
查询区间不同数目的个数
//求区间不同数的个数
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int N = 30007; //最多N个数
const int M = N * 100;
int n, q, tot; //n个数,q次询问
int a[N]; //原始数组
//T[i]保存[1,i]中有多少个不同的数
int T[M], lson[M], rson[M], c[M]; //c数组可能就是主席树要维护的值,比如本题维护区间数的个数
int build(int l, int r) { //返回[l,r]的父节点。建一棵非叶子节点都有两个孩子的树
int rt = tot++; //tot从0开始,用的节点数。最终tot=2*n-1
c[rt] = 0;
if (l != r) {
int mid = l + r >> 1;
lson[rt] = build(l, mid); //lson用来保存父亲节点rt的左孩子的索引
rson[rt] = build(mid + 1, r);
}
return rt;
}
int update(int rt, int pos, int val) {//这里用了迭代更新,也可以递归更新
int newRoot = tot++, tmp = newRoot; //newRoot申请新的节点
c[newRoot] = c[rt] + val;//主席树可以加减。同构
int l = 1, r = n;
while (l < r) {//二分建树,只是写法形式上和 mid = l + r >> 1然后左右建树不同
int mid = l + r >> 1;
if (pos <= mid) {
lson[newRoot] = tot++;//新建左子树
rson[newRoot] = rson[rt];//右子树复用前一棵线段树的,这样就节省了空间,不至于MLE
newRoot = lson[newRoot];
rt = lson[rt];
r = mid;
}
else {
rson[newRoot] = tot++;
lson[newRoot] = lson[rt];
newRoot = rson[newRoot]; //上句要用到,newRoot,so,不能慌着更新newRoot
rt = rson[rt];//更新当前根节点
l = mid + 1;
}
c[newRoot] = c[rt] + val;//新节点的值,由之前的节点更新而来
}
return tmp; //返回新建树的根节点
}
//询问l到pos有多少个不同的数, rt = T[l]
int query(int rt, int pos) {
int res = 0;
int l = 1, r = n;
while (pos < r) {
int mid = l + r >> 1;
if (pos <= mid) {//在左子树中查
r = mid;
rt = lson[rt];
}
else { //在右子树中搜
res += c[lson[rt]];
rt = rson[rt];
l = mid + 1;
}
}
return res + c[rt];
}
int main()
{
while (~scanf("%d", &n)) {
tot = 0;
for (int i = 1; i <= n; ++i)
scanf("%d", a + i);
T[n + 1] = build(1, n); //先建一棵树
map<int, int> mp;
for (int i = n; i >= 1; --i) {
if (mp.find(a[i]) == mp.end()) {
T[i] = update(T[i + 1], i, 1); //每次调用都新建一棵线段树。update返回的是新建树的根节点
}
else {
int tmp = update(T[i + 1], mp[a[i]], -1); //若a[i]不是很大,可用数组代替映射
T[i] = update(tmp, i, 1);
}
mp[a[i]] = i; //若之前存在a[i],那么a[i]将被覆盖了,第二值保存的是较小的i
}
scanf("%d", &q);
while (q--) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(T[l], r));//由l确定在那棵线段树中查询
}
}
return 0;
}
\
\
\
\
\
\
\