主席树

参考资料:
http://blog.csdn.net/metalseed/article/details/8045038
http://www.cnblogs.com/oyking/p/3230296.html
可持久化的线段树 - 包括函数式线段树实现方法 其中著名的有第一类主席树和第二类主席树
前缀和主席树套一个树状数组或者直接用树状数组add每个点就得到第二类主席树
第一类主席树可以解决区间k小问题,时间复杂度为单次logn一共(n+q)log(n) 空间复杂度为 nlogn
第二类主席树可以解决带修改的区间k小问题,时间复杂度为单次lognlogn 共nlogn+qlognlogn 或者不用前缀和 (n+q)lognlogn 空间复杂度为 qlognlogn 或者不用前缀和 (q+n)logn*logn 用垃圾回收重复利用空间可以 去掉一个logn的空间 nlogn 因为树状数组没有必要在修改之后再用修改之前的总共就只要开nlogn的空间因为c数组n个就可以了每个一条顶到叶子的链logn的空间,可以直接使用或者回收利用(并不能减少。。。0 0)
这里的单科线段树总节点数直接用n表示了,n和tot一个量级
区间k小,线段树的每个节点统计的是每个rank值的数量
前缀和代表的前缀1-i添加了前i个点的线段树
第i棵树可以利用到第i-1棵,因为差的就是接下来要插入的i点的rank值,只会有一条顶到底的链节点值不同前一个,所以不用建出所有完整的线段树,每颗前缀和的树只会增加logn节点,其它的利用以前节点,共nlogn空间
关键,巧妙是因为每棵树的形态都是一样的,范围也是一样的,可以直接利用以前,又可以直接加减,所以树状数组也能利用
求k小就是看左孩子数量是不是>=k 是走左边不是走右边
主席树是离线的,因为要看查询和修改里的值,把对应值离散化到n个点的值里来,另外划分树和离线区间分治也能解决区间k小问题

看了下离线区间分治,因为离线,每个排名都确定了,把修改操作分成两份某个点数量-1,某个点数量+1,初始化操作算某个点数量+1,然后算对左边区间和右边区间的贡献,先只算左边区间右边分治递归,把当前每次查询操作排名<=mid的贡献算出来,然后判断与每次查询的值比较如果>=k这个查询就划分到左边也就是【l,mid】的排名里,否则划分到右边【mid+1,r】排名里,划分到右边的累计一下左边【l,mid】的贡献值,修改操作直接看目标rank点在哪边继续划分到左右,然后分治递归,最后哪些查询操作在l==r里,把对应答案索引记录一下就行了。因为有修改,要计算某次查询的贡献还是要用到树状数组,修改算贡献的时候判断一下rank<=mid,才处理add就行了,算完每次查询贡献保存临时值后,要把树状数组还原回去,感觉清掉也行,以便之后分治继续使用。。。(好吧其实这就是划分树)
附一下zoj2112的划分树方法链接:
http://www.cnblogs.com/chanme/p/4493455.html

hdu 4010 第一类主席树 这个是求区间中数值小于h的有多少个的,用了map还是咋回事要c++交,g++跪

#include <cstdio>
#include <memory>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <cassert>
#include <string>
#include <ctime>
#include <map>
#include <queue>
#include <algorithm>
#include <iostream>
#include <cassert>
#include <set>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define req(i,a,b) for(int i=a;i>=b;i--)
#define rp(i,a) for(int i=head[a];i+1;i=edge[i].next)
#define cl(a,b) memset(a,b,sizeof a);
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mod 10007
const int inf = ~0u >> 2;
const ll INF = (1LL << 62) - 1;
double eps = 1e-12;
const int N = 100005 + 5;
const int M = 100005;

#pre sum president tree

int n, m, d;
int a[N];
int rnk[N];
int root[N];
int sz = 0;
struct Node {
	int sum;
	int ch[2];
}T[N * 30];
void insert(int &num, int k, int l, int r) {
	++sz;
	T[sz] = T[num]; num = sz;
	T[num].sum++;
	if (l == r) {
		return;
	}
	int m = (l + r) >> 1;
	if (k <= m) {
		insert(T[num].ch[0], k, l, m);
	}
	else {
		insert(T[num].ch[1], k, m + 1, r);
	}
}
int query(int i, int j, int k, int l, int r) {
	if (r <= k)
		return T[j].sum - T[i].sum;
	if (l > k) return 0;
	int m = (l + r) >> 1;
	return query(T[i].ch[0], T[j].ch[0], k, l, m) + query(T[i].ch[1], T[j].ch[1], k, m + 1, r);

}
struct Seg {
	int l, r, h;
}q[N];
int val[N * 5];
int cnt = 0;
map<int, int> mp;
int main() {
	int t;
	scanf("%d", &t);
	for (int x = 1; x <= t; x++) {
		for (int i = 0; i <= sz; i++)T[i].ch[0] = T[i].ch[1] = T[i].sum = 0;
		cnt = 0;
		sz = 0;
		mp.clear();

		scanf("%d%d", &n, &m);

		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			val[cnt++] = a[i];
		}
		for (int i = 0; i < m; i++) {
			int l, r, h;
			scanf("%d%d%d", &l, &r, &h);
			q[i].l = l;
			q[i].r = r;
			q[i].h = h;
			val[cnt++] = h;
		}
		sort(val, val + cnt);
		int tot = 0;
		rnk[0] = ++tot;
		for (int i = 1; i < cnt; i++) {
			if (val[i] != val[i - 1])
				rnk[i] = ++tot;
			else
				rnk[i] = tot;
		}
		for (int i = 0; i<cnt; i++)
			mp[val[i]] = rnk[i];
		root[0] = 0;
		for (int i = 1; i <= n; i++) {
			root[i] = root[i - 1];
			insert(root[i], mp[a[i]], 1, tot);
		}
		printf("Case %d:
", x);
		for (int i = 0; i < m; i++) {
			printf("%d
", query(root[q[i].l], root[q[i].r + 1], mp[q[i].h], 1, tot));
		}
	}
	return 0;
}

zoj 2112 把map改成了2分的方法才过了,这里map占内存不小,另外发现垃圾回收或者重复利用并不能绝对减少空间,每次走的路径并不以一定相同。。。这里好像不重复利用也不递归插入都可以过。。。map坑啊。。。
zoj 2112 第二类主席树 这里函数式的思想,我就当它是打了个Pack包,可以进行+=了以及包装了lt() rt() 和强转 int 或者说把线段树的根节点当成树状数组的每个节点来用 树状数组的用法就像个函数?瞎比比

#include <cstdio>
#include <memory>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <cassert>
#include <string>
#include <ctime>
#include <map>
#include <queue>
#include <algorithm>
#include <iostream>
#include <cassert>
#include <set>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define req(i,a,b) for(int i=a;i>=b;i--)
#define rp(i,a) for(int i=head[a];i+1;i=edge[i].next)
#define cl(a,b) memset(a,b,sizeof a);
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mod 10007
const int inf = ~0u >> 2;
const ll INF = (1LL << 62) - 1;
double eps = 1e-12;
const int N = 60005 + 5;
const int M = 10005;

int n, m, d;
int a[50005];
int rnk[N];
int root[N];
int croot[N];
int sz = 0, tot = 0;
void insert(int &num, int val, int k, int l, int r);
struct Node {
	int sum;
	int ch[2];
}T[2500010];
void insert(int &num, int val, int k, int l, int r,int flag) {
	if (flag==0||num==0) {
		T[++sz] = T[num]; num = sz;
	}
	T[num].sum += val;
	if (l == r) {
		return;
	}
	int m = (l + r) >> 1;
	if (k <= m) {
		insert(T[num].ch[0], val, k, l, m, flag);
	}
	else {
		insert(T[num].ch[1], val, k, m + 1, r, flag);
	}
}
//int insert(int &num, int val, int k, int l, int r, int flag) {
//	if (flag == 0 || num == 0) {
//		T[++sz] = T[num]; num = sz;
//	}
//	T[num].sum += val;
//	int x = num;
//	while (l < r) {
//		int mid = (l + r) >> 1;
//		if (k <= mid) {
//			int y = x;
//			x = T[x].ch[0];
//			if (flag == 0 || x == 0) {
//				T[++sz] = T[x];
//				x = sz;
//			}
//			T[x].sum += val;
//			T[y].ch[0] = x;
//			r = mid;
//		}
//		else {
//			int y = x;
//			x = T[x].ch[1];
//			if (flag == 0 || x == 0) {
//				T[++sz] = T[x];
//				x = sz;
//			}
//			T[x].sum += val;
//			T[y].ch[1] = x;
//			l = mid + 1;
//		}
//	}
//	return num;
//}
int query(int i, int j, int k, int l, int r) {
	if (k > T[j].sum - T[i].sum)
		k = T[i].sum - T[i].sum;
	if (l == r)
		return l;
	int m = (l + r) >> 1;
	if (k <= T[T[j].ch[0]].sum - T[T[i].ch[0]].sum)
		return query(T[i].ch[0], T[j].ch[0], k, l, m);
	else
		return query(T[i].ch[1], T[j].ch[1], k - (T[T[j].ch[0]].sum - T[T[i].ch[0]].sum), m + 1, r);
}
void add(int x, int num, int d) {
	while (x <= tot) {
		insert(croot[x], d, num, 1, tot,1);
		x += x&-x;
	}
}
struct Pack {
	vector<int> v;
	Pack() {}
	Pack(int x) { v.push_back(x); }
	void operator +=(int x) {
		v.push_back(x);
	}
	operator int()const {//得到左孩子和
		int ret = 0;
		for(int i=0;i<v.size();i++)
			ret += T[T[v[i]].ch[0]].sum;
		return ret;
	}
	void lt() {
		for (int i = 0; i < v.size(); i++)
			v[i] = T[v[i]].ch[0];
	}
	void rt() {
		for (int i = 0; i < v.size(); i++)
			v[i] = T[v[i]].ch[1];
	}
};
Pack sum(int x, int k) {
	Pack ret;
	while (x>0) {
		ret += croot[x];
		x -= x&-x;
	}
	return ret;
}
int binSearch(int l, int r, int k) {
	Pack treesl = sum(l, k);
	Pack treesr = sum(r, k);
	Pack presl = Pack(root[l]);
	Pack presr = Pack(root[r]);
	int xl = 1, xr = tot;
	while (xl<xr) {
		int mid = (xl + xr) >> 1;
		int t = treesr - treesl + presr - presl;
		if (t < k) {
			treesr.rt(); treesl.rt(); presr.rt(); presl.rt();
			k -= t;
			xl = mid + 1;
		}
		else {
			treesr.lt(); treesl.lt(); presr.lt(); presl.lt();
			xr = mid;
		}
	}
	return xl;
}
struct Seg {
	int l, r, h, x;
}q[M];
int val[N];
int cnt = 0;
//map<int, int> mp;
int Hash(int x) {
	return lower_bound(val, val + tot, x) - val+1;
}
int main() {
	int t;
	scanf("%d", &t);
	for (int x = 1; x <= t; x++) {
		for (int i = 0; i <= sz; i++)T[i] = T[0];
		cnt = 0;
		sz = 0;
		tot = 0;
		//mp.clear();

		scanf("%d%d", &n, &m);

		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			val[cnt++] = a[i];
		}
		for (int i = 0; i < m; i++) {
			char op[2];
			scanf("%s", op);
			if (op[0] == 'Q') {
				int l, r, h;
				scanf("%d%d%d", &l, &r, &h);
				q[i].l = l;
				q[i].r = r;
				q[i].h = h;
				if (l<1||r>n||l>r||h<1 || h>r - l + 1)
					assert(false);
			}
			else {
				int x, h;
				scanf("%d%d", &x, &h);
				q[i].x = x;
				q[i].h = h;
				val[cnt++] = h;
				q[i].l = -inf;
			}
		}
		sort(val, val + cnt);
		rnk[0] = ++tot;
		for (int i = 1; i < cnt; i++) {
			if (val[i] != val[i - 1])
				rnk[i] = ++tot;
			else
				rnk[i] = tot;
		}
		for (int i = 0; i <= tot; i++)croot[i] = 0;
		unique(val, val + cnt);
		/*for (int i = 0; i<cnt; i++)
			mp[val[i]] = rnk[i];*/
		root[0] = 0;
		for (int i = 1; i <= n; i++) {
			root[i] = root[i - 1];
			insert(root[i], 1, Hash(a[i]), 1, tot, 0);
		}
		for (int i = 0; i < m; i++) {
			if (q[i].l!=-inf) {
				printf("%d
", val[binSearch(q[i].l - 1, q[i].r, q[i].h) - 1]);
				//printf("%d
", query(root[q[i].l], root[q[i].r + 1], mp[q[i].h], 1, tot));
			}
			else {
				add(q[i].x, Hash(a[q[i].x]), -1);
				a[q[i].x] = q[i].h;
				add(q[i].x, Hash(q[i].h), 1);
			}
		}
		for (int i = 1; i <= tot; i++)
			croot[i] = 0, val[i] = 0, a[i] = 0;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/HaibaraAi/p/6375866.html