zoukankan      html  css  js  c++  java
  • 线段树_模版

    线段树

      线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
    使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。

    区间修改与求和

    给出数的个数n以及操作数q:
    对于q:
    1 x y z 令区间[x, y]增加z
    2 x y 求区间和

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #define LL long long
     5 using namespace std;
     6 const int maxn=1e5+10;
     7 int n, q;
     8 LL a[maxn];
     9 LL sum[maxn<<2], li[maxn<<2];
    10 
    11 void _push_up(int rt) {
    12     sum[rt]=sum[rt<<1]+sum[(rt<<1)|1];
    13 }//向上更新,求区间和
    14 
    15 void _setup(int l, int r, int rt) {
    16     li[rt]=0;
    17     if(l==r){
    18         sum[rt]=a[l];
    19         return ;
    20     }
    21     int mid=(l+r)>>1;
    22     _setup(l, mid, rt<<1);
    23     _setup(mid+1, r, (rt<<1)|1);
    24     _push_up(rt);
    25 }//建树
    26 
    27 void _push_down(int rt, int m) {
    28     if(li[rt]) {
    29         li[rt<<1]+=li[rt];
    30         li[(rt<<1)|1]+=li[rt];
    31         sum[rt<<1]+=li[rt]*(m-(m>>1));
    32         sum[(rt<<1)|1]+=li[rt]*(m>>1);
    33         li[rt]=0;
    34     }
    35 }//将li标记向下更新,传递给小区间
    36 
    37 void _add(int x, int y, LL z, int l ,int r, int rt) {
    38     if(x<=l && y>=r) {
    39         li[rt]+=z;
    40         sum[rt]+=z*(r-l+1);
    41         return ;
    42     }
    43     _push_down(rt, r-l+1); //边算边更新,节省复杂度
    44     int mid=(l+r)>>1;
    45     if(x<=mid) _add(x, y, z, l, mid, rt<<1);
    46     if(y>mid) _add(x, y, z, mid+1, r, (rt<<1)|1);
    47     _push_up(rt);
    48 }
    49 
    50 LL _query(int x, int y, int l, int r, int rt) {
    51     if(x<=l && y>=r) {
    52         return sum[rt];
    53     }
    54     _push_down(rt, r-l+1); //求和时再顺便更新一次
    55     int mid=(l+r)>>1;
    56     LL ans=0;
    57     if(x<=mid) ans+=_query(x, y, l, mid, rt<<1);
    58     if(y>mid) ans+=_query(x, y, mid+1, r, (rt<<1)|1); //'夹'区间
    59     return ans;
    60 }
    61 
    62 int main() {
    63     scanf("%d%d", &n, &q);
    64     for(int i=1; i<=n; i++) {
    65         scanf("%lld", a+i);
    66     }
    67     _setup(1, n, 1);
    68     for(int i=0; i<q; i++) {
    69         int ls;
    70         scanf("%d", &ls);
    71         if(ls==1) {
    72             int x, y;
    73             LL z;
    74             scanf("%d%d%lld", &x, &y, &z);
    75             _add(x, y, z, 1, n, 1);
    76         } else if(ls==2) {
    77             int x, y;
    78             scanf("%d%d", &x, &y);
    79             printf("%lld
    ", _query(x, y, 1, n ,1));
    80         }
    81     }
    82     return 0;
    83 }
    View Code

    区间最大值

    描述:第1行给出数的个数n,操作数m
    第2行为n个数
    第3到m+3-1行有两个数a,b,求区间[a,b]中的最大数

    样例:

    Input

    8 8
    9 3 1 7 5 6 0 8
    1 6
    1 5
    2 7
    2 6
    1 8
    4 8
    3 7
    1 8

    Output

    9
    9
    7
    7
    9
    8
    7
    9

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #define CL(X,N) memset(X, N, sizeof(X))
     5 #define LL long long
     6 #define ls root<<1
     7 #define rs root<<1|1
     8 using namespace std;
     9 const int maxn=1e5+10;
    10 int n, m;
    11 int a[maxn], t[maxn<<2];
    12 
    13 void _pushup(int root) {
    14     t[root]=max(t[ls], t[rs]);
    15 }
    16 
    17 void _set(int l, int r, int root) {
    18     if(l==r) {
    19         t[root]=a[l];
    20         return ;
    21     }
    22     int mid=(l+r)>>1;
    23     _set(l, mid, ls);
    24     _set(mid+1, r, rs);
    25     _pushup(root);
    26 }
    27 
    28 int _query(int l, int r, int root, int a, int b) {
    29     if(a<=l && b>=r) {
    30         return t[root];
    31     }
    32     int mid=(l+r)>>1, ans=0;
    33     if(a<=mid) ans=_query(l, mid, ls, a, b);
    34     if(b>mid) ans=max(ans, _query(mid+1, r, rs, a, b));
    35     return ans;
    36 }
    37 
    38 int main() {
    39     scanf("%d%d", &n, &m);
    40     for(int i=1; i<=n; i++) {
    41         scanf("%d", a+i);
    42     }
    43     _set(1, n, 1);
    44     for(int i=0; i<m; i++) {
    45         int a, b;
    46         scanf("%d%d", &a, &b);
    47         printf("%d
    ", _query(1, n, 1, a, b));
    48     }
    49     return 0;
    50 }
    View Code

    指针版:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #define CL(X,N) memset(X, N, sizeof(X))
     5 #define LL long long
     6 using namespace std;
     7 const int maxn=1e5+10;
     8 int n, m;
     9 int a[maxn];
    10 struct Bit {
    11     int l, r, data;
    12     Bit *ls, *rs;
    13     Bit(){
    14         l=0, r=0, data=0;
    15         ls=rs=NULL;
    16     }
    17 }*t;
    18 
    19 #define lc root->ls
    20 #define rc root->rs
    21 void _pushup(Bit *root) {
    22     root->data=max(lc->data, rc->data);
    23 }
    24 
    25 void _set(int l, int r, Bit *root) {
    26     root->l=l;
    27     root->r=r;
    28     if(l==r) {
    29         root->data=a[l];
    30         return ;
    31     }
    32     if(!lc) lc=new Bit;
    33     if(!rc) rc=new Bit;
    34     int mid=(l+r)>>1;
    35     _set(l, mid, lc);
    36     _set(mid+1, r, rc);
    37     _pushup(root);
    38 }
    39 
    40 int _query(Bit *root, int a, int b) {
    41     if(!root) return 0;
    42     int l=root->l, r=root->r;
    43     if(a<=l && b>=r) {
    44         return root->data;
    45     }
    46     int mid=(l+r)>>1, ans=0;
    47     if(a<=mid) ans=_query(lc, a, b);
    48     if(b>mid) ans=max(ans, _query(rc, a, b));
    49     return ans;
    50 }
    51 
    52 int main() {
    53     t=new Bit;
    54     scanf("%d%d", &n, &m);
    55     for(int i=1; i<=n; i++) {
    56         scanf("%d", a+i);
    57     }
    58     _set(1, n, t);
    59     for(int i=0; i<m; i++) {
    60         int x, y;
    61         scanf("%d%d", &x, &y);
    62         printf("%d
    ", _query(t, x, y));
    63     }
    64     return 0;
    65 }
    View Code

    为什么写指针?

    因为不容易MLE(不存在访问无效内存),但是写起来会需要小心一点

     

  • 相关阅读:
    Binary Tree Zigzag Level Order Traversal
    Add Binary
    Subsets II
    Subsets
    Minimum Depth of Binary Tree
    node Cannot enqueue Quit after invoking quit.
    微擎快速修改数量实例(异步)
    destoon 分页
    ajax里面使用this方法
    微擎系统 微信支付 get_brand_wcpay_request:fail
  • 原文地址:https://www.cnblogs.com/normaldisk/p/9126858.html
Copyright © 2011-2022 走看看