参考资料:
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;
}