主席树练习-CSDN博客

26 阅读1分钟

\

\

spoj D-query

查询区间不同数目的个数

//求区间不同数的个数
#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;
}


\

\

\

\

\

\

\