zoj 2112 Dynamic Rankings 带修改区间第K大 动态主席树
pass
首先,个人觉得把这个数据结构理解成树状数组套主席树是十分不严谨的。主席树的本质是可持久化权值线段树与前缀和思想的结合。而动态主席树是可持久化权值线段树与树状数组思想的结合。并非树套树般的泾渭分明的叠加。
其次,大概讲下对动态主席树的理解。我们静态主席树中,第i个版本维护的是[1,i]的权值线段树,我们利用前缀和的思想,通过y的版本-x的版本得到[x,y]的权值线段树,从而剥离出一颗对应区间的权值线段树。我们考虑在这个情况下,如果需要修改第a[i]的值,那么从i,i+1,i+2......n的每个版本的线段树都要做出修改,显然代价难以承受。我们发现静态主席树继承了前缀和修改复杂度高,查询复杂度低的特点。我们想,能否利用其他数据结构的思想,与可持久化权值线段树结合呢?树状数组很好地满足了这个要求,修改和查询复杂度均为log级别,而且依旧有类似前缀和性质(这点我们从树状数组求区间和就能清晰地看出),只不过与某个位置相关的前缀,变成了log级别,这为我们修改每个前缀提供了可能。每当我们在i的x值+1时,我们就把i,i+lowbit(i)...的各个版本权值为x的下标都+1。当我们查询时,我们通过树状数组的思想,求出[1,x-1]和[1,y]的前缀和,再相减获得[x,y]的权值线段树即可。这道题需要离散化,注意权值线段树因为涉及到排名问题,原递增序列离散化后仍应该为递增序列。具体操作建议仔细阅读代码。
最后,这道题出题人卡了空间,动态主席树每次插入会比静态主席树的插入多消耗log级别的空间,而初始的数据规模又是操作的数据规模的5倍,因此用静态主席树操作最初的序列空间优势明显。所以标算是对最初的序列做了静态主席树,而对后来的修改做了动态主席树,这两颗主席树的下标权值是完全相同的。对于修改,我们只操作动态主席树,将原有的值对应权值下标-1,将新的值对应权值下标+1。对于询问,我们在两棵主席树上同步的进行,每次将动态主席树的左孩子大小之和与静态主席树的左孩子大小之和整体求和后,与排名k比较大小,从而决定两棵主席树是同步进入左子树还是右子树。
我会提供一份纯动态主席树,但因为空间问题无法AC的代码,学习动态主席树可以先看这个。然后提供一份静态动态主席树混合的AC代码。
动态主席树RE代码:
1 #include <cstdio> 2 #include <map> 3 #include <algorithm> 4 using namespace std; 5 const int MAXN = 61000,MAXM = 2500000; 6 map <int,int> n2h; 7 int sum,tot,n,q,T,tre; 8 int cnt[MAXM],lson[MAXM],rson[MAXM]; 9 int h2n[MAXN],vec[MAXN],a[MAXN],l2v[MAXN],qry[MAXN][4]; 10 //cnt[x]:线段树节点x所代表区间的和 11 //h2n[x]:将哈希值x转化为数值 n2h[x]:将数值x转化为哈希值 12 //vec[x]:树状数组中,序列位置x的版本的根 tre 空白线段树的根 13 //a[x]:原序列x处元素 14 //l2v[x] 表示在当前权值下标范围内,序列位置x从根走到哪个节点了。 15 //这是最难理解的一个数组。当没有权值下标限制时(或者说限制是[1,tot]时),l2v[x] = vec[x]。 16 int lowbit(int x) 17 {//lowbit 18 return x & (-x); 19 } 20 int get_sum(int x) 21 {//树状数组思想,求出当前权值下标范围,动态主席树中[1,x](序列位置)之和。 22 int res = 0; 23 while (x > 0) 24 { 25 res += cnt[lson[l2v[x]]]; 26 x -= lowbit(x); 27 } 28 return res; 29 } 30 void build(int &x,int l,int r) 31 {//权值线段树建树,传引用简化代码。 32 x = ++sum; 33 cnt[x] = 0; 34 if (l == r) 35 return; 36 int mid = l + r >> 1; 37 build(lson[x],l,mid); 38 build(rson[x],mid + 1,r); 39 } 40 void insert(int pre,int &x,int l,int r,int loc,int ad) 41 {//插入新的版本,传引用简化代码。 42 x = ++sum; 43 cnt[x] = cnt[pre] + ad; 44 if (l == r) 45 return; 46 int mid = l + r >> 1; 47 if (loc <= mid) 48 { 49 rson[x] = rson[pre]; 50 insert(lson[pre],lson[x],l,mid,loc,ad); 51 } 52 else 53 { 54 lson[x] = lson[pre]; 55 insert(rson[pre],rson[x],mid + 1,r,loc,ad); 56 } 57 } 58 int query(int left,int right,int l,int r,int k) 59 {//询问区间[left,right],权值下标限制范围为[l,r],排名为k的权值下标 60 if (l == r) 61 return l; 62 int mid = l + r >> 1,tmp = get_sum(right) - get_sum(left); 63 if (tmp >= k) 64 { 65 for (int i = left;i;i -= lowbit(i)) 66 l2v[i] = lson[l2v[i]]; 67 for (int i = right;i;i -= lowbit(i)) 68 l2v[i] = lson[l2v[i]]; 69 return query(left,right,l,mid,k); 70 } 71 for (int i = left;i;i -= lowbit(i)) 72 l2v[i] = rson[l2v[i]]; 73 for (int i = right;i;i -= lowbit(i)) 74 l2v[i] = rson[l2v[i]]; 75 return query(left,right,mid + 1,r,k - tmp); 76 } 77 void add(int x,int val,int ad) 78 {//在序列位置x,权值val,增加ad 79 while (x <= n) 80 { 81 insert(vec[x],vec[x],1,tot,val,ad); 82 x += lowbit(x); 83 } 84 } 85 int main() 86 { 87 for (scanf("%d",&T);T;T--) 88 { 89 tot = 0; 90 sum = 0; 91 n2h.clear(); 92 scanf("%d%d",&n,&q); 93 for (int i = 1;i <= n;i++) 94 { 95 scanf("%d",&a[i]); 96 h2n[++tot] = a[i]; 97 } 98 char s[10]; 99 for (int i = 1;i <= q;i++) 100 { 101 scanf("%s",s); 102 if (s[0] == 'Q') 103 { 104 qry[i][0] = 0; 105 scanf("%d%d%d",&qry[i][1],&qry[i][2],&qry[i][3]); 106 }else 107 { 108 qry[i][0] = 1; 109 scanf("%d%d",&qry[i][1],&qry[i][2]); 110 h2n[++tot] = qry[i][2]; 111 } 112 } 113 sort(h2n + 1,h2n + tot + 1); 114 tot = unique(h2n + 1,h2n + tot + 1) - (h2n + 1); 115 for (int i = 1;i <= tot;i++) 116 n2h[h2n[i]] = i; 117 build(tre,1,tot); 118 for (int i = 1;i <= n;i++) 119 vec[i] = tre; 120 for (int i = 1;i <= n;i++) 121 add(i,n2h[a[i]],1); 122 for (int mi = 1;mi <= q;mi++) 123 { 124 if (qry[mi][0] == 0) 125 { 126 for (int i = qry[mi][1] - 1;i;i -= lowbit(i)) 127 l2v[i] = vec[i]; 128 for (int i = qry[mi][2];i;i -= lowbit(i)) 129 l2v[i] = vec[i]; 130 printf("%d\n",h2n[query(qry[mi][1] - 1,qry[mi][2],1,tot,qry[mi][3])]); 131 } 132 else 133 { 134 add(qry[mi][1],n2h[a[qry[mi][1]]],-1); 135 add(qry[mi][1],n2h[qry[mi][2]],1); 136 a[qry[mi][1]] = qry[mi][2]; 137 } 138 } 139 } 140 return 0; 141 }
静态动态主席树混合AC代码:
1 #include <cstdio> 2 #include <map> 3 #include <algorithm> 4 using namespace std; 5 const int MAXN = 61000,MAXM = 2500000; 6 map <int,int> n2h; 7 int sum,tot,n,q,T; 8 int cnt[MAXM],lson[MAXM],rson[MAXM]; 9 int h2n[MAXN],tre[MAXN],vec[MAXN],a[MAXN],l2v[MAXN],qry[MAXN][4]; 10 int lowbit(int x) 11 { 12 return x & (-x); 13 } 14 int get_sum(int x) 15 { 16 int res = 0; 17 while (x > 0) 18 { 19 res += cnt[lson[l2v[x]]]; 20 x -= lowbit(x); 21 } 22 return res; 23 } 24 void build(int &x,int l,int r) 25 { 26 x = ++sum; 27 cnt[x] = 0; 28 if (l == r) 29 return; 30 int mid = l + r >> 1; 31 build(lson[x],l,mid); 32 build(rson[x],mid + 1,r); 33 } 34 void insert(int pre,int &x,int l,int r,int loc,int ad) 35 { 36 x = ++sum; 37 cnt[x] = cnt[pre] + ad; 38 if (l == r) 39 return; 40 int mid = l + r >> 1; 41 if (loc <= mid) 42 { 43 rson[x] = rson[pre]; 44 insert(lson[pre],lson[x],l,mid,loc,ad); 45 } 46 else 47 { 48 lson[x] = lson[pre]; 49 insert(rson[pre],rson[x],mid + 1,r,loc,ad); 50 } 51 } 52 int query(int lx,int rx,int left,int right,int l,int r,int k) 53 { 54 if (l == r) 55 return l; 56 int mid = l + r >> 1,tmp = get_sum(right) - get_sum(left) + cnt[lson[rx]] - cnt[lson[lx]]; 57 if (tmp >= k) 58 { 59 for (int i = left;i;i -= lowbit(i)) 60 l2v[i] = lson[l2v[i]]; 61 for (int i = right;i;i -= lowbit(i)) 62 l2v[i] = lson[l2v[i]]; 63 return query(lson[lx],lson[rx],left,right,l,mid,k); 64 } 65 for (int i = left;i;i -= lowbit(i)) 66 l2v[i] = rson[l2v[i]]; 67 for (int i = right;i;i -= lowbit(i)) 68 l2v[i] = rson[l2v[i]]; 69 return query(rson[lx],rson[rx],left,right,mid + 1,r,k - tmp); 70 } 71 void add(int x,int val,int ad) 72 { 73 while (x <= n) 74 { 75 insert(vec[x],vec[x],1,tot,val,ad); 76 x += lowbit(x); 77 } 78 } 79 int main() 80 { 81 for (scanf("%d",&T);T;T--) 82 { 83 tot = 0; 84 sum = 0; 85 n2h.clear(); 86 scanf("%d%d",&n,&q); 87 for (int i = 1;i <= n;i++) 88 { 89 scanf("%d",&a[i]); 90 h2n[++tot] = a[i]; 91 } 92 char s[10]; 93 for (int i = 1;i <= q;i++) 94 { 95 scanf("%s",s); 96 if (s[0] == 'Q') 97 { 98 qry[i][0] = 0; 99 scanf("%d%d%d",&qry[i][1],&qry[i][2],&qry[i][3]); 100 }else 101 { 102 qry[i][0] = 1; 103 scanf("%d%d",&qry[i][1],&qry[i][2]); 104 h2n[++tot] = qry[i][2]; 105 } 106 } 107 sort(h2n + 1,h2n + tot + 1); 108 tot = unique(h2n + 1,h2n + tot + 1) - (h2n + 1); 109 for (int i = 1;i <= tot;i++) 110 n2h[h2n[i]] = i; 111 build(tre[0],1,tot); 112 for (int i = 1;i <= n;i++) 113 insert(tre[i - 1],tre[i],1,tot,n2h[a[i]],1); 114 for (int i = 1;i <= n;i++) 115 vec[i] = tre[0]; 116 for (int mi = 1;mi <= q;mi++) 117 { 118 if (qry[mi][0] == 0) 119 { 120 for (int i = qry[mi][1] - 1;i;i -= lowbit(i)) 121 l2v[i] = vec[i]; 122 for (int i = qry[mi][2];i;i -= lowbit(i)) 123 l2v[i] = vec[i]; 124 printf("%d\n",h2n[query(tre[qry[mi][1] - 1],tre[qry[mi][2]],qry[mi][1] - 1,qry[mi][2],1,tot,qry[mi][3])]); 125 } 126 else 127 { 128 add(qry[mi][1],n2h[a[qry[mi][1]]],-1); 129 add(qry[mi][1],n2h[qry[mi][2]],1); 130 a[qry[mi][1]] = qry[mi][2]; 131 } 132 } 133 } 134 return 0; 135 }