个人理解:
每个新的线段树的一个结点保存的是1...位置 i中的数字在相应的区间上有几个。
然后我们用r-(l-1)得到的就是l...r上的中字在相应的区间中出现了几个。
题目1 POJ2104
题目大意:静态查询区间第K小值。
裸的可持久化线段树。

1 #include <cstdlib>
2 #include <iostream>
3 #include <algorithm>
4 #include <cstdio>
5 #include <cstring>
6
7 using namespace std;
8 const int N = 100000 + 5;
9
10 struct SegTree {
11 int l, r, size;
12 }Node[N * 30];
13
14 struct data {
15 int v, pos;
16 bool operator < (const data &k) const {
17 return v < k.v;
18 }
19 }a[N];
20
21 int n, m, rank[N], l, r, k, root[N], tot;
22
23 void build(int &o, int l, int r) {
24 o = ++ tot;
25 Node[o].l = Node[o].r = Node[o].size = 0;
26 if(l >= r) return;
27 int mid = (l + r) >> 1;
28 build(Node[o].l, l, mid);
29 build(Node[o].r, mid + 1, r);
30 }
31
32 void update(int pre, int &o, int l, int r, int kth) {
33 o = ++ tot;
34 Node[o] = Node[pre];
35 Node[o].size ++;
36 if(l >= r) return;
37 int mid = (l + r) >> 1;
38 if(kth <= mid)
39 update(Node[pre].l, Node[o].l, l, mid, kth);
40 else
41 update(Node[pre].r, Node[o].r, mid + 1, r, kth);
42 }
43
44 int query(int r1, int r2, int l, int r, int kth) {
45 if(l >= r) return l;
46 int lc = Node[Node[r2].l].size - Node[Node[r1].l].size;
47 int mid = (l + r) >> 1;
48 if(lc >= kth)
49 return query(Node[r1].l, Node[r2].l, l, mid, kth);
50 else
51 return query(Node[r1].r, Node[r2].r, mid + 1, r, kth - lc);
52 }
53
54 int main() {
55 scanf("%d%d", &n, &m);
56 for(int i = 1; i <= n; ++ i) {
57 scanf("%d", &a[i].v);
58 a[i].pos = i;
59 }
60 sort(a + 1, a + n + 1);
61 for(int i = 1; i <= n; ++ i)
62 rank[a[i].pos] = i;
63 build(root[0], 1, n);
64 for(int i = 1; i <= n; ++ i)
65 update(root[i - 1], root[i], 1, n, rank[i]);
66 for(int i = 1; i <= m; ++ i) {
67 scanf("%d%d%d", &l, &r, &k);
68 printf("%d\n", a[query(root[l - 1], root[r], 1, n, k)].v);
69 }
70 return 0;
71 }
题目2 SPOJ Count On a Tree 1
题目大意:
给一个树,求路径(u,v)上的第k小值。
算法讨论: 可持久化线段树。
我们考虑序列的做法是一个元素在前一个元素上建立新的线段树。那么对于一个树来说,就是在父亲的基础上建立一棵新的线段树。
所有我们有如下的做法:先搞出每个点的权值的rank,然后对树DFS,求出DFS序,则每个点就以各自的DFS序为一个新根,建立在FA的DFS序上。
在查询的时候,用U和V的线段树权值减去LCA和FA(LCA)的线段树权值即可。
坑点:一定要注意去重。然后再搞RANK。
代码:

1 #include <cstdlib>
2 #include <iostream>
3 #include <cstring>
4 #include <algorithm>
5 #include <cstdio>
6
7 using namespace std;
8 const int N = 100000 + 5;
9 inline int read() {
10 int x = 0;
11 char c = getchar();
12 while(!isdigit(c)) c = getchar();
13 while(isdigit(c)) {
14 x = x * 10 + c - '0';
15 c = getchar();
16 }
17 return x;
18 }
19
20 int n, m, tail, tot, cnt, tim;
21 int v[N], seq[N], num[N], seg[N], fa[N];
22 int f[N][20], head[N], depth[N], rank[N], root[N];
23
24 struct Edge {
25 int from, to, next;
26 }edges[N << 1];
27
28 struct SegTree {
29 int l, r, size;
30 }Node[N * 40];
31
32 void insert(int from, int to) {
33 ++ cnt;
34 edges[cnt].from = from; edges[cnt].to = to;
35 edges[cnt].next = head[from]; head[from] = cnt;
36 }
37
38 void dfs(int u, int ff) {
39 fa[u] = ff;
40 ++ tim; num[u] = tim; seg[tim] = u;
41 for(int i = head[u]; i; i = edges[i].next) {
42 int v = edges[i].to;
43 if(v != ff) {
44 depth[v] = depth[u] + 1;
45 dfs(v, u);
46 }
47 }
48 }
49
50 void prepare() {
51 memset(f, -1, sizeof f);
52 for(int i = 1; i <= n; ++ i) f[i][0] = fa[i];
53 for(int j = 1; (1 << j) <= n; ++ j) {
54 for(int i = 1; i <= n; ++ i) {
55 if(f[i][j - 1] != -1) {
56 f[i][j] = f[f[i][j - 1]][j - 1];
57 }
58 }
59 }
60 }
61
62 int lca(int a, int b) {
63 int i;
64 if(depth[a] < depth[b]) swap(a, b);
65 for(i = 0; (1 << i) <= depth[a]; ++ i);
66 -- i;
67 for(int j = i; j >= 0; -- j)
68 if(depth[a] - depth[b] >= (1 << j))
69 a = f[a][j];
70 if(a == b) return a;
71 for(int j = i; j >= 0; -- j) {
72 if(f[a][j] != -1 && f[a][j] != f[b][j]) {
73 a = f[a][j]; b = f[b][j];
74 }
75 }
76 return f[a][0];
77 }
78
79 void build(int &o, int l, int r) {
80 o = ++ tot;
81 Node[o].l = Node[o].r = Node[o].size = 0;
82 if(l >= r) return;
83 int mid = (l + r) >> 1;
84 build(Node[o].l, l, mid);
85 build(Node[o].r, mid + 1, r);
86 }
87
88 void update(int pre, int &o, int l, int r, int kth) {
89 o = ++ tot;
90 Node[o] = Node[pre];
91 Node[o].size ++;
92 if(l >= r) return;
93 int mid = (l + r) >> 1;
94 if(kth <= mid)
95 update(Node[pre].l, Node[o].l, l, mid, kth);
96 else
97 update(Node[pre].r, Node[o].r, mid + 1, r, kth);
98 }
99
100 int query(int r1, int r2, int r3, int r4, int l, int r, int kth) {
101 if(l >= r) return l;
102 int lc = Node[Node[r1].l].size + Node[Node[r2].l].size - Node[Node[r3].l].size - Node[Node[r4].l].size;
103 int mid = (l + r) >> 1;
104 if(lc >= kth)
105 return query(Node[r1].l, Node[r2].l, Node[r3].l, Node[r4].l, l, mid, kth);
106 else return query(Node[r1].r, Node[r2].r, Node[r3].r, Node[r4].r, mid + 1, r, kth - lc);
107 }
108
109 int Q(int x, int y, int z) {
110 int Lca = lca(x, y);
111 return seq[query(root[num[x]], root[num[y]], root[num[Lca]], root[num[fa[Lca]]], 1, tail, z)];
112 }
113
114 int main() {
115 //int __size__ = 64 << 20; // 64MB
116 //char *__p__ = (char*)malloc(__size__) + __size__;
117 //__asm__("movl %0, %%esp\n" :: "r"(__p__));
118 int x, y, z;
119 n = read(); m = read();
120 for(int i = 1; i <= n; ++ i) {
121 v[i] = read();
122 seq[i] = v[i];
123 }
124 sort(seq + 1, seq + n + 1);
125 tail = unique(seq + 1, seq + n + 1) - seq - 1;
126 for(int i = 1; i <= n; ++ i)
127 rank[i] = lower_bound(seq + 1, seq + tail + 1, v[i]) - seq;
128 for(int i = 1; i < n; ++ i) {
129 x = read(); y = read();
130 insert(x, y); insert(y, x);
131 }
132 depth[1] = 1; num[0] = 0; seg[0] = 0;
133 dfs(1, 0); prepare();
134 build(root[0], 1, tail);
135 for(int i = 1; i <= n; ++ i) {
136 int t = seg[i];
137 update(root[num[fa[t]]], root[i], 1, tail, rank[t]);
138 }
139 bool flag = false;
140 for(int i = 1; i <= m; ++ i) {
141 x = read(); y = read(); z = read();
142 //x ^= lastans;
143 if(flag)
144 printf("\n%d", Q(x, y, z));
145 else {
146 printf("%d", Q(x, y, z));
147 flag = true;
148 }
149 }
150 return 0;
151 }
题目3 SPOJ Count On a Tree II (还没写呢)
题目大意:
给一个树,每个点都有一个权值。求(u,v)路径上有多少本质不同的权值。
n <= 4W, m <= 10W
算法讨论:
首先感觉这个题可以树上莫队搞掉。哪天开心了写一发,今天先贴可持久化的做法。
题目4 HDU4417 SuperMario
题目大意:
给定一个数列,求区间L_R内小于等于H的数有多少。
算法讨论:
分块?别想了你,32M内存,卡掉你不在话下。而且多组数据,O(Nsqrt(N)*T)估计险过吧。还是来YY正解。
可持久化线段树。首先把序列离散化建立可持久化线段树。
然后对于每个要查询的数,先查询出其在原序列中的Rank,由于是小于等于它的数有多少,所以 upper_bound。
否则就Lower_boud。接着我们在权值线段树上走。如果这个数比当前区间的mid大,就加上左子树的size差然后去右边查询。
否则就去左边查询。自己还傻傻的每次都去叶子结点查询。
自己要注意的几个地方:
首先自己离散化之后的数组大小和权值线段树的右边界老不改,如果还用N,就是这题14遍的TLE。
还就是如果当前要查询的数是整个序列中最小的,那么返回值是-1,则要记得判断为0.
鸣谢SMZ大神指导。

1 #include <cstdlib>
2 #include <cstdio>
3 #include <cstring>
4 #include <algorithm>
5 #include <iostream>
6
7 using namespace std;
8
9 const int N = 100000 + 5;
10 inline int read() {
11 int x = 0;
12 char c = getchar();
13 while(!isdigit(c)) c = getchar();
14 while(isdigit(c)) {
15 x = x * 10 + c - '0';
16 c = getchar();
17 }
18 return x;
19 }
20
21 struct data {
22 int v, pos;
23 bool operator < (const data &k) const {
24 return v < k.v;
25 }
26 }a[N];
27
28 struct SegTree {
29 int size, l, r;
30 }Node[N * 19];
31
32 int n, m, tot, ans, tail;
33 int rk[N], root[N], v[N], seq[N];
34
35 void build(int &o, int l, int r) {
36 o = ++ tot;
37 Node[o].l = Node[o].r = Node[o].size = 0;
38 if(l >= r) return;
39 int mid = (l + r) >> 1;
40 build(Node[o].l, l, mid);
41 build(Node[o].r, mid + 1, r);
42 }
43
44 void update(int pre, int &o, int l, int r, int kth) {
45 o = ++ tot;
46 Node[o] = Node[pre];
47 Node[o].size ++;
48 if(l >= r) return;
49 int mid = (l + r) >> 1;
50 if(kth <= mid)
51 update(Node[pre].l, Node[o].l, l, mid, kth);
52 else
53 update(Node[pre].r, Node[o].r, mid + 1, r, kth);
54 }
55
56 int query(int r1, int r2, int l, int r, int kth) {
57 if(l > r) return 0;
58 if(l == r)
59 return Node[r2].size - Node[r1].size;
60 int mid = (l + r) >> 1;
61 if(kth > mid) {
62 return (Node[Node[r2].l].size - Node[Node[r1].l].size) + query(Node[r1].r, Node[r2].r, mid + 1, r, kth);
63 }
64 else {
65 return query(Node[r1].l, Node[r2].l, l, mid, kth);
66 }
67 }
68
69 void Q(int x, int y, int z, int kth) {
70 ans = query(root[x - 1], root[y], 1, tail, kth);
71 }
72
73 int main() {
74 int t, x, y, z, tt = 0, zk;
75 t = read();
76 while(t --) {
77 for(int i = 1; i <= tot; ++ i)
78 Node[i].size = Node[i].l = Node[i].r = 0;
79 tot = 0;
80 ++ tt;
81 printf("Case %d:\n", tt);
82 n = read(); m = read();
83 for(int i = 1; i <= n; ++ i) {
84 v[i] = read();
85 seq[i] = v[i];
86 }
87 sort(seq + 1, seq + n + 1);
88 tail = unique(seq + 1, seq + n + 1) - seq - 1;
89 for(int i = 1; i <= n; ++ i)
90 rk[i] = lower_bound(seq + 1, seq + tail + 1, v[i]) - seq;
91 build(root[0], 1, tail);
92 for(int i = 1; i <= n; ++ i)
93 update(root[i - 1], root[i], 1, tail, rk[i]);
94 for(int i = 1; i <= m; ++ i) {
95 x = read(); y = read(); z = read();
96 x ++; y ++;
97 zk = upper_bound(seq + 1, seq + tail + 1, z) - seq - 1;
98 if(zk > 0)
99 Q(x, y, z, zk);
100 else ans = 0;
101 printf("%d\n", ans);
102 }
103 }
104 return 0;
105 }
题目5 51nod 1175 区间第K大。
裸题啊。
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50000 + 5;
int n, tail, cnt, m;
int a[N], que[N], rank[N], root[N];
struct SegMent {
int l, r, size;
}Node[N * 20];
void build(int &o, int l, int r) {
o = ++ cnt;
Node[o].l = Node[o].r = Node[o].size = 0;
if(l >= r) return;
int mid = (l + r) >> 1;
build(Node[o].l, l, mid);
build(Node[o].r, mid + 1, r);
}
void update(int o, int &p, int l, int r, int kth) {
p = ++ cnt;
Node[p] = Node[o];
Node[p].size ++;
if(l >= r) return;
int mid = (l + r) >> 1;
if(kth <= mid)
update(Node[o].l, Node[p].l, l, mid, kth);
else
update(Node[o].r, Node[p].r, mid + 1, r, kth);
}
int query(int r1, int r2, int l, int r, int kth) {
if(l >= r) return l;
int lc = Node[Node[r2].l].size - Node[Node[r1].l].size;
int mid = (l + r) >> 1;
if(lc >= kth)
return query(Node[r1].l, Node[r2].l, l, mid, kth);
else
return query(Node[r1].r, Node[r2].r, mid + 1, r, kth - lc);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) {
scanf("%d", &a[i]);
que[++ tail] = a[i];
}
sort(que + 1, que + tail + 1);
tail = unique(que + 1, que + tail + 1) - que - 1;
for(int i = 1; i <= n; ++ i) rank[i] = lower_bound(que + 1, que + tail + 1, a[i]) - que;
build(root[0], 1, tail);
for(int i = 1; i <= n; ++ i)
update(root[i - 1], root[i], 1, tail, rank[i]);
scanf("%d", &m);
for(int i = 1; i <= m; ++ i) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
l ++; r ++;
printf("%d\n", que[query(root[l - 1], root[r], 1, tail, (r - l + 1) - k + 1)]);
}
system("pause");
return 0;
}
来源:https://www.cnblogs.com/sxprovence/p/5349774.html
