主席树 刷题记录

一些调试经验

  • 要修改某个时间点的树时一定要再整一个新的树出来,不能在上一个树的根节点就这样修改,因为某一个时间点的树永远连着以前的,改了的话会对前面的版本产生影响
  • cnt一直在累加,用cnt赋值的时候注意再注意。

 

洛谷P3834 【模板】可持久化线段树 1(主席树)

  • 静态区间第 k
  • 先把输入的a[i]离散化,假设最后有k个数,再从1~k建树
  • 然后从1~n遍历,v[i]表示数i的个数,每个i新建一颗线段树
  • 对于询问(l,r),用root[r]的树减去root[l-1]的树即可得到给(l,r)建的线段树
  • 在(l,r)建的线段树里查询第k小即可。
  • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 200010
     3 
     4 using namespace std;
     5 int a[nmax], c[nmax], ls[nmax*40], rs[nmax*40], v[nmax*40], root[nmax];
     6 struct px{
     7     int x, id;
     8     bool operator < (const px& tmp) const{
     9         return tmp.x > x;
    10     }
    11 }b[nmax];
    12 int cnt=0;
    13 
    14 int build(int l, int r){
    15     cnt++;
    16     int u = cnt;
    17     if(l==r) return u;
    18     int mid = (l+r)>>1 ;
    19     ls[u] = build(l, mid);
    20     rs[u] = build(mid+1, r);
    21     return u;
    22 }
    23 
    24 void upd(int u, int k, int l, int r, int u2){
    25     v[u2] = v[u]+1;
    26     if(l==r) return;
    27     int mid = (l+r)>>1 ;
    28     if(k<=mid) {
    29         rs[u2] = rs[u];
    30         ls[u2] = ++cnt;
    31         upd(ls[u], k, l, mid, ls[u2]);
    32     }else{
    33         ls[u2] = ls[u];
    34         rs[u2] = ++cnt;
    35         upd(rs[u], k, mid+1, r, rs[u2]);
    36     }
    37 }
    38 int myfind(int u, int u2, int k, int l, int r){
    39     if(l==r) return l;
    40     int num = v[ ls[u2] ] - v[ ls[u] ];
    41     int mid = (l+r)>>1;
    42     if(k<=num) return myfind(ls[u], ls[u2], k, l, mid);
    43     else return myfind(rs[u], rs[u2], k-num, mid+1, r);
    44 }
    45 
    46 int main(){
    47     int n, m;
    48     scanf("%d%d", &n, &m);
    49     for (int i=1; i<=n; i++) {
    50         scanf("%d", &b[i].x);
    51         b[i].id = i;
    52     } 
    53     sort(b+1, b+n+1);
    54     int idx=1;
    55     c[ b[1].id ] = 1;
    56     a[1] = b[1].x;
    57     for (int i=2; i<=n; i++) {
    58         if(b[i].x!=b[i-1].x) idx++;
    59         c[ b[i].id ] = idx;
    60         a[ idx ] = b[i].x;
    61     }
    62     build(1, idx);
    63     root[0] = 1;
    64     for (int i=1; i<=n; i++) {
    65         root[i] = ++cnt;
    66         upd(root[i-1], c[i], 1, idx, root[i]);
    67     }
    68     int il, ir, ik;
    69     while(m--){
    70         scanf("%d%d%d", &il, &ir, &ik);
    71         int t = myfind(root[il-1], root[ir], ik, 1, idx);
    72         printf("%d\n", a[t] );
    73     }
    74     return 0;
    75 }
    இ௰இ

     

hduoj5919 Sequence II 2016ccpcchangchunj

  • 输入一个数组,有m次询问
  • 强制在线,每个询问(l,r),假如(l,r)有k个不同的数,要求输出第(k+1)/2个数的下标
  • 倒着从n~1遍历建树,因为每一次要更新两个,一个是i位置要加1,一个是pre[i]要减1
  • pre[i]是i位置上的数上一次出现的位置(倒着遍历)
  • 这样的话,相当于每次只是某个数第一次出现的地方是1,其他都是0,每个询问直接查询root[l]就行
  • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 200100
     3 
     4 using namespace std;
     5 int n, m, cnt=0;
     6 int pos[nmax]={0}, a[nmax], pre[nmax], v[nmax*40], root[nmax], ls[nmax*40], rs[nmax*40];
     7 
     8 int build(int l, int r){
     9     int u=++cnt;
    10     v[u] = 0;
    11     if(l==r) return u;
    12     int mid = (l+r)>>1;
    13     ls[u] = build(l, mid);
    14     rs[u] = build(mid+1, r);
    15     return u;
    16 }
    17 
    18 void upd(int u, int k, int l, int r, int u2, int p){
    19     v[u2] = v[u]+p;
    20     if(l==r) return;
    21     int mid = (l+r)>>1 ;
    22     if(k<=mid) {
    23         rs[u2] = rs[u];
    24         ls[u2] = ++cnt;
    25         upd(ls[u], k, l, mid, ls[u2], p);
    26     }else{
    27         ls[u2] = ls[u];
    28         rs[u2] = ++cnt;
    29         upd(rs[u], k, mid+1, r, rs[u2], p);
    30     }
    31 }
    32 
    33 int getlen(int u, int l, int r, int l1, int r1){
    34     if(l1<=l && r1>=r) return v[u];
    35     int ans = 0;
    36     int mid = (l+r)>>1;
    37     if(l1 <= mid) ans += getlen(ls[u], l, mid, l1, r1);
    38     if(r1 > mid) ans += getlen(rs[u], mid+1, r, l1, r1);
    39     return ans;
    40 }
    41 
    42 int myfind(int u, int k, int l, int r){
    43     if(l==r) return l;
    44     int num = v[ ls[u] ];
    45     int mid = (l+r)>>1;
    46     if(k<=num) return myfind(ls[u], k, l, mid);
    47     else return myfind(rs[u], k-num, mid+1, r);
    48 }
    49 
    50 int main(){
    51     int kas;
    52     cin >> kas;
    53     for (int cas=1; cas<=kas; cas++) {
    54         cnt = 0;
    55         scanf("%d%d", &n, &m);
    56         for (int i=1; i<=n; i++) { scanf("%d", &a[i]); pos[ a[i] ] = 0; }
    57         for (int i=n; i>=1; i--) {
    58             pre[i] = pos[ a[i] ];
    59             pos[ a[i] ] = i;
    60         }
    61         build(1, n);
    62         root[n+1] = 1;
    63         for (int i=n; i>=1; i--) {
    64             root[i] = ++cnt;
    65             upd(root[i+1], i, 1, n, root[i], 1);
    66             if(pre[i]) {
    67                 int u = ++cnt;
    68                 upd(root[i], pre[i], 1, n, u, -1);
    69                 root[i] = u;
    70             }
    71         }
    72         printf("Case #%d:", cas);
    73         int il, ir, li, ri, t = 0, k;
    74         while(m--){
    75             scanf("%d%d", &il, &ir);
    76             li = min( (il+t)%n+1 , (ir+t)%n+1 );
    77             ri = max( (il+t)%n+1 , (ir+t)%n+1 );
    78             k = getlen(root[li], 1, n, li, ri);
    79             t = myfind(root[li], (k+1)/2, 1, n);
    80             printf(" %d", t);
    81         }
    82         printf("\n");
    83     }
    84     return 0;
    85 }
    (*Φ皿Φ*)

     

posted @ 2019-11-25 16:08  连昵称都不能重复  阅读(117)  评论(0编辑  收藏  举报