zoukankan      html  css  js  c++  java
  • 主席树

    参考资料:
    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;
    }
    
  • 相关阅读:
    poj 3304线段与直线相交
    poj 1039 几何没思路
    zoj 1010 (线段相交判断+多边形求面积)
    poj 1654 Area (多边形求面积)
    poj 3348--Cows(凸包求面积)
    zoj 2107&&hdu 1007最近点对问题
    Codeforces Round #260 (Div. 2)AB
    MMORPG大型游戏设计与开发(part1 of net)
    MMORPG大型游戏设计与开发(规范)
    MMORPG大型游戏设计与开发(构架)
  • 原文地址:https://www.cnblogs.com/HaibaraAi/p/6375866.html
Copyright © 2011-2022 走看看