主席树练习

来源:互联网 发布:蓝牙怎么传软件 编辑:程序博客网 时间:2024/05/29 07:26



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-1c[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];//右子树复用前一棵线段树的,这样就节省了空间,不至于MLEnewRoot = lson[newRoot];rt = lson[rt];r = mid;}else {rson[newRoot] = tot++;lson[newRoot] = lson[rt];newRoot = rson[newRoot]; //上句要用到,newRoot,so,不能慌着更新newRootrt = 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;}