题意:给定一个区间,求这个区间第k大的数,支持单点修改。
思路:主席树真是个神奇的东西.........速度很快但是也有一个问题就是占用内存的很大,一般来说支持单点修改的主席树套树状数组空间复杂度为O(n*logn*logn), 如果查询较少的话,可以初始的时候用一颗静态主席树,这样空间复杂度可以降为O(n*logn+q*logn*logn),勉强可以过zoj这道题。
这道题看了好久好久才懂...网上题解一般就几句话.......下面是自己的一些理解。
首先静态主席树这个东西其实比较好懂,就是对于每个前缀[1,1],[1,2]....[1,n]都建一颗线段树,将数据离散化后,在线段树中统计区间内所有值出现的次数,每一个线段树都是相同的形态,那么这样我们得到一个很好的性质,这些线段树可以“相减”,假设当前查询的是区间[l,r]内第k大的值,那么我们用前缀[1, r]这个线段树和前缀[1, l-1]这颗线段树通过相减加上二分就可以找到答案。由于相邻两颗线段树最多只有logn个节点不同,我们没有必要完全新建一颗线段树,只需要把相同的结点用指针指一下就行,然后新建logn个结点,这样一来时空复杂度为n*logn。
对于动态查询的主席树,如果修改一个点的值,我们肯定不能修改所有包含这个点的线段树,时间复杂度无法承受。
那么我们就可以套上树状数组,个人感觉这里比较难懂。
树状数组的每个节点都是一颗线段树,但这棵线段树不再保存每个前缀的信息了,而是由树状数组的sum函数计算出这个前缀的信息,那么显而易见这棵线段树保存的是辅助数组S的值,即S=A[i-lowbit+1]+...+A[i],其中A[i]表示值为i的元素出现的次数。
那么对于每次修改,我们要修改树状数组上的logn棵树,对于每棵树,我们要修改logn个结点,所以时空复杂度为
O((n+q)*logn*logn),由于这道题n比较大,查询次数q比较小,所以我们可以初始时建立一颗静态的主席树,树状数组只保存每次修改的信息,那么时空复杂度降为了O(n*logn+q*logn*logn)。
代码参考自:http://www.cnblogs.com/kuangbin/p/3308118.html
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #include<ctime> #define eps 1e-6 #define LL long long #define pii (pair<int, int>) //#pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; //zoj 2104 动态主席树修改+静态主席树 const int maxn = 60000+1000; const int M = 2400000; int n, q, m, tot; int a[maxn], t[maxn]; int T[maxn], lson[M], rson[M], c[M]; int S[maxn]; struct Query { int kind; int l, r, k; } query[10010]; void Init_hash(int k) { sort(t, t+k); m = unique(t, t+k) - t; } int Hash(int x) { return lower_bound(t, t+m, x) - t; } int build(int l, int r) { int root = tot++; c[root] = 0; if(l != r) { int mid = (l+r) >> 1; lson[root] = build(l, mid); rson[root] = build(mid+1, r); } return root; } int Insert(int root, int pos, int val) { int newroot = tot++, tmp = newroot; int l = 0, r = m-1; c[newroot] = c[root] + val; while(l < r) { int mid = (l+r)>>1; if(pos <= mid) { lson[newroot] = tot++; rson[newroot] = rson[root]; newroot = lson[newroot]; root = lson[root]; r = mid; } else { rson[newroot] = tot++; lson[newroot] = lson[root]; newroot = rson[newroot]; root = rson[root]; l = mid+1; } c[newroot] = c[root] + val; } return tmp; } int lowbit(int x) { return x&(-x); } int use[maxn]; void add(int x, int pos, int d) { while(x < n) { S[x] = Insert(S[x], pos, d); x += lowbit(x); } } int Sum(int x) { int ret = 0; while(x > 0) { ret += c[lson[use[x]]]; x -= lowbit(x); } return ret; } int Query(int left, int right, int k) { int left_root = T[left-1], right_root = T[right]; int l = 0, r = m-1; for(int i = left-1; i; i -= lowbit(i)) use[i] = S[i]; for(int i = right; i; i -= lowbit(i)) use[i] = S[i]; while(l < r) { int mid = (l+r) >> 1; int tmp = Sum(right) - Sum(left-1) + c[lson[right_root]] - c[lson[left_root]]; if(tmp >= k) { r = mid; for(int i = left-1; i; i -= lowbit(i)) use[i] = lson[use[i]]; for(int i = right; i; i -= lowbit(i)) use[i] = lson[use[i]]; left_root = lson[left_root]; right_root = lson[right_root]; } else { l = mid + 1; k -= tmp; for(int i = left-1; i; i -= lowbit(i)) use[i] = rson[use[i]]; for(int i = right; i; i -= lowbit(i)) use[i] = rson[use[i]]; left_root = rson[left_root]; right_root = rson[right_root]; } } return l; } int main() { //freopen("input.txt", "r", stdin); int Tcase; cin >> Tcase; while(Tcase--) { scanf("%d%d", &n, &q); tot = 0; m = 0; for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); t[m++] = a[i]; } char op[10]; for(int i = 0;i < q;i++) { scanf("%s",op); if(op[0] == 'Q') { query[i].kind = 0; scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k); } else { query[i].kind = 1; scanf("%d%d", &query[i].l, &query[i].r); t[m++] = query[i].r; } } Init_hash(m); T[0] = build(0, m-1); for(int i = 1; i <= n; i++) T[i] = Insert(T[i-1], Hash(a[i]), 1); for(int i = 1; i <= n; i++) S[i] = T[0]; for(int i = 0; i < q; i++) { if(query[i].kind == 0) printf("%d\n",t[Query(query[i].l,query[i].r,query[i].k)]); else { add(query[i].l, Hash(a[query[i].l]), -1); add(query[i].l, Hash(query[i].r), 1); a[query[i].l] = query[i].r; } } } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。