Kth number

划分树虽然可以做,但是代码不好记。

看某人blog学习了主席树的简单操作。

 

引用某大牛的话来解释一下主席树:

所谓主席树呢,就是对原来的数列[1..n]的每一个前缀[1..i](1≤i≤n)建立一棵线段树,线段树的每一个节点存某个前缀[1..i]中属于区间[L..R]的数一共有多少个(比如根节点是[1..n],一共i个数,sum[root] = i;根节点的左儿子是[1..(L+R)/2],若不大于(L+R)/2的数有x个,那么sum[root.left] = x)。若要查找[i..j]中第k大数时,设某结点x,那么x.sum[j] - x.sum[i - 1]就是[i..j]中在结点x内的数字总数。而对每一个前缀都建一棵树,会MLE,观察到每个[1..i]和[1..i-1]只有一条路是不一样的,那么其他的结点只要用回前一棵树的结点即可,时空复杂度为O(nlogn)。

 

——具体看代码(有详细注释)

 1 #include <cstdio>
 2 #include <algorithm>
 3 #define lc son[now][0], l, mid
 4 #define rc son[now][1], mid + 1, r
 5 
 6 using namespace std;
 7 
 8 const int N = 100000 + 5;
 9 
10 int T, n, q, tot;
11 int a[N], b[N], son[20 * N][2], sum[20 * N], rt[20 * N];
12 //主席树第 i 棵树存的是 a[0] - a[i] 的树(前缀和) 
13 //主席树一个节点 sum 存的是当前线段所包含的数的个数。。晕。 
14 //rt 是每一棵树的根节点编号
15 //a 保存原数组,b 保存排序后的数组 
16 
17 //注意 & 的使用
18 inline void build(int &now, int l, int r)
19 {
20     now = ++tot;
21     sum[now] = 0;
22     if(l == r) return;
23     int mid = (l + r) >> 1;
24     build(lc);
25     build(rc);
26 }
27 
28 inline void update(int &now, int l, int r, int last, int x)
29 {
30     now = ++tot;
31     //继承上个节点的孩子 
32     son[now][0] = son[last][0];
33     son[now][1] = son[last][1];
34     //继承上一个节点的 sum 并加上当前所加入的数的个数(就是1) 
35     sum[now] = sum[last] + 1; 
36     if(l == r) return;
37     int mid = (l + r) >> 1;
38     if(x <= mid) update(lc, son[now][0], x);
39     else update(rc, son[now][1], x);
40 }
41 
42 //因为每个节点存的是有多少个数在当前区间
43 //求区间 [1,3] 即求 rt[3] - rt[0] 内的数
44 //可通过递归二分解决 
45 inline int query(int s, int t, int l, int r, int x)
46 {
47     if(l == r) return l;
48     int mid = (l + r) >> 1, cnt = sum[son[t][0]] - sum[son[s][0]];
49     if(x <= cnt) return query(son[s][0], son[t][0], l, mid, x);
50     else return query(son[s][1], son[t][1], mid + 1, r, x - cnt);
51 }
52 
53 int main()
54 {
55     int i, x, y, z, sz;
56     scanf("%d", &T);
57     while(T--)
58     {
59         scanf("%d %d", &n, &q);
60         for(i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
61         sort(b + 1, b + n + 1);
62         sz = unique(b + 1, b + n + 1) - (b + 1);//求不同的数一共有几个,即区间大小 
63         tot = 0;
64         build(rt[0], 1, sz);
65         //用每个元素在 b 中的坐标重置 a 数组(离散化) 
66         for(i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + sz + 1, a[i]) - b;
67         for(i = 1; i <= n; i++) update(rt[i], 1, sz, rt[i - 1], a[i]);
68         while(q--)
69         {
70             scanf("%d %d %d", &x, &y, &z);
71             printf("%d\n", b[query(rt[x - 1], rt[y], 1, sz, z)]);
72         }
73     }
74     return 0;
75 }
View Code

相关文章:

  • 2022-12-23
  • 2021-12-25
  • 2021-07-23
  • 2022-12-23
  • 2021-08-26
  • 2022-01-29
猜你喜欢
  • 2021-05-17
  • 2021-08-30
  • 2021-11-02
  • 2021-05-18
  • 2022-01-26
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案