zoukankan      html  css  js  c++  java
  • 线段树-小总结

    递归版线段树

    中心思想:

    1.自上而下(最顶节点递归到叶子)
    2.p,l,r三者作为整体效果
    3.命令的传递,执行,反馈

    构成函数:
    up,build,f,dn,mdy,qry

    int n;
    aLL sum;
    arr tg, a;
    namespace seg
    {
    #define ls(x) (x << 1)
    #define rs(x) (x << 1 | 1)
    //反馈
    inline void up(int p)
    {
      sum[p] = sum[ls(p)] + sum[rs(p)];
    }
    //对于每个节点,清空标记,截半,接收子节点效果
    void build(int p, int l, int r)
    {
      tg[p] = 0;
      if (l == r)
      {
        sum[p] = a[l];
        return;
      }
      int mid = l + r >> 1;
      build(ls(p), l, mid);
      build(rs(p), mid + 1, r);
      up(p);
    }
    //执行命令:tg,sum
    inline void f(int p, int l, int r, int k)
    {
      tg[p] += k;
      sum[p] += k * (r - l + 1);
    }
    //传递命令:截半,自身命令消除
    inline void dn(int p, int l, int r)
    {
      int mid = l + r >> 1;
      f(ls(p), l, mid, tg[p]);
      f(rs(p), mid + 1, r, tg[p]);
      tg[p] = 0;
    }
    //加上子区间,截半,传递,反馈
    inline void mdy(int ql, int qr, int l, int r, int p, int x)
    {
      if (ql <= l && r <= qr)
      {
        sum[p] += x * (r - l + 1);
        tg[p] += x;
        return;
      }
      dn(p, l, r);
      int mid = l + r >> 1;
      if (ql <= mid)
        mdy(ql, qr, l, mid, ls(p), x);
      if (qr > mid)
        mdy(ql, qr, mid + 1, r, rs(p), x);
      up(p);
    }
    //加上子区间,传递命令
    inline ll qry(int ql, int qr, int l, int r, int p)
    {
      if (ql <= l && r <= qr)
        return sum[p];
      ll res = 0;
      up(p, l, r);
      int mid = l + r >> 1;
      if (mid >= ql)
        res += qry(ql, qr, l, mid, ls(p));
      if (mid < qr)
        res += qry(ql, qr, mid + 1, r, rs(p));
      return res;
    }
    } // namespace seg

     

     非递归线段树

    中心思想:
    1.自下而上(且每次遍历均是从最底层到最高层)
    2. 用蓝色节点金字塔式包裹紫色待处理区间
    3.sgt的出现(因为左右两侧需要将其裹住,所以左右两端必须空出两个空节点,sgt为大于等于n+2的最小2^k数)

    由五大函数构成:
    build mdy1 qry1 mdyn qryn

    注意:
    此方法不能处理优先级问题,如混合乘法和加法(因为自下而上的顺序的限制)

    int n; 
    aLL sum;
    arr tg, a;
    ll sgt;
    namespace seg
    {
    //sum的下标中,1~sgt-1都是非叶节点,sgt+1~sgt+n为叶节点,忽略sgt这个点
    void build(int n)
    {
      sgt = 1;
      while (sgt < n + 2) 
        sgt <<= 1;
      For(i, 1, n) sum[i + sgt] = a[i];
      FFor(i, sgt - 1, 1) sum[i] = sum[i << 1] + sum[i << 1 | 1], tg[i] = 0; //注意这里是sgt-1不是n-1
    }
    void mdy1(int p, ll x)
    {
      for (int s = p + sgt; s; s >>= 1)
        sum[s] += x;
    }
    ll qry1(int l, int r)
    {
      ll res = 0;
      for (int s = l + sgt - 1, t = r + sgt + 1; s ^ t ^ 1; s >>= 1, t >>= 1) //s,t都是边界相邻点,s^t^1是判断他们是不是共父节点,s>>=1,是不停向上走
      {
        if (~s & 1)
          res += sum[s ^ 1]; //s^1是抓处理区域节点
        if (t & 1)
          res += sum[t ^ 1];
      }
      return res;
    }
    inline void mdyn(int l, int r, ll c)
    {
      int s, t, x = 1;   //x是当前处理个数
      ll Ln = 0, Rn = 0;  //Ln,Rn分别记录左右两端共抓取多少处理区域数
      for (s = l + sgt - 1, t = r + sgt + 1; s ^ t ^ 1; s >>= 1, t >>= 1, x <<= 1)
      {
        sum[s] += Ln * c; //下层传递给当前的这层
        sum[t] += Rn * c;
        if (~s & 1)
          sum[s ^ 1] += x * c, tg[s ^ 1] += c, Ln += x;
        if (t & 1)
          sum[t ^ 1] += x * c, tg[t ^ 1] += c, Rn += x;
      }
      for (; s; s >>= 1, t >>= 1) //当前这层传递给上层
      {
        sum[s] += Ln * c;
        sum[t] += Rn * c;
      }
    }
    ll qryn(int l, int r)
    {
      ll res = 0;
      ll Ln = 0, Rn = 0;
      int x = 1, s, t;
      for (s = l + sgt - 1, t = r + sgt + 1; s ^ t ^ 1; s >>= 1, t >>= 1, x <<= 1)
      {
        if (tg[s])
          res += tg[s] * Ln;
        if (tg[t])
          res += tg[t] * Rn;
        if (~s & 1)
          res += sum[s ^ 1], Ln += x;
        if (t & 1)
          res += sum[t ^ 1], Rn += x;
      }
      for (; s; s >>= 1, t >>= 1)
      {
        res += Ln * tg[s];
        res += Rn * tg[t];
      }
      return res;
    }
    } 
    // namespace seg

    dijkstra+线段树优化 (求最短路,效率高于堆优化)

    中心思想:
    1.非递归版线段树(tr存的是dis最小的实际序号)
    2.dijkstra(循环n次,每次找最小的dis更新)

    namespace seg
    {
      //tr存的是dis最小的实际序号
    int tr[N << 2], sgt;
    inline void build(int n)
    {
      sgt = 1;
      while (sgt < n + 2)
        sgt <<= 1;
      //dis[0]=inf,dis不能memset全程,最后要留一些0,使得dis[N+1]=0
      tr[0] = N+1; 
    }
    inline void clr() { For(i, 1, sgt << 1) tr[i] = 0; }
    inline int cmp(const int &a, const int &b) { return dis[a] < dis[b] ? a : b; }
    //因为小所以改变,不是加法啦,把能大于w的节点都改成u
    inline void mdy(int u, int w)
    {
      for (int p = u + sgt; dis[tr[p]] > w; p >>= 1)
        tr[p] = u;
      dis[u] = w;
    }
    inline void del(int u)
    {
      //因为0点本身效果是dis[0]=inf,所以这里相当于就删掉了
      tr[u+=sgt] = 0;
      for (u >>= 1; u; u >>= 1)
        tr[u] = cmp(tr[u << 1], tr[u << 1 | 1]);
    }
    void dij(int s, int *fi, int *Dis)
    {
      dis = Dis;
      // me(dis, 0x3f);不能直接这么写,反正会出问题,覆盖n+1的四倍最好
      For(i, 0, 4*(n+1)) dis[i] = 2147483647; 
      // memset(dis, 127, 4 * (n + 1)); 
      clr();
      mdy(s, 0);
      //循环n次,每次找离起点最近的dis,取出那个节点u,del掉(方便为下一次处理铺垫),然后对u相连所有边mdy松弛
      For(i, 1, n)
      {
        int u = tr[1];
        del(u);
        for (int v, p = fi[u]; p != -1; p = e[p].nx)
          if (dis[v = e[p].to] > dis[u] + e[p].w)
            mdy(v, e[p].w + dis[u]);
      }
    }
    } // namespace seg
    main:
    ce = 1;
    me(fi1, -1);
    add(u, v, w, fi1);
    seg::build(n);
    seg::dij(st, fi1, dis1);

    洛谷线段树模板题(乘法和加法混合,只能用递归版)

    https://www.luogu.org/problemnew/show/P3373

    (2,9,10数据加强过,mmp,一直70分,干脆把int全改成ll就过了)

    根据运算级优先顺序,先算乘法再算加法

    namespace seg
    {
    #define ls(x) (x << 1)
    #define rs(x) (x << 1 | 1)
    aLL tr, tg1, tg2;
    inline void up(ll p)
    {
      tr[p] = (tr[ls(p)] + tr[rs(p)]) % mod;
    }
    void build(ll l, ll r, ll p)
    {
      tg1[p] = 0, tg2[p] = 1;
      if (l == r)
      {
        tr[p] = a[l];
        return;
      }
      ll mid = l + r >> 1;
      build(l, mid, ls(p));
      build(mid + 1, r, rs(p));
      up(p);
    }
    //关键!!!!tr,tg1都是先乘再加 inline
    void f(ll l, ll r, ll p, ll k1, ll k2) { mulmod(tr[p], k2); addmod(tr[p], k1 * (r - l + 1)); mulmod(tg2[p], k2); mulmod(tg1[p], k2); addmod(tg1[p], k1); } inline void dn(ll l, ll r, ll p) { ll mid = l + r >> 1; f(l, mid, ls(p), tg1[p], tg2[p]); f(mid + 1, r, rs(p), tg1[p], tg2[p]); tg1[p] = 0; tg2[p] = 1; } inline void add(ll ql, ll qr, ll l, ll r, ll p, ll x) { if (ql <= l && r <= qr) { addmod(tr[p], (r - l + 1) * x); addmod(tg1[p], x); return; } if (tg1[p] || tg2[p] != 1) dn(l, r, p); ll mid = l + r >> 1; if (ql <= mid) add(ql, qr, l, mid, ls(p), x); if (qr > mid) add(ql, qr, mid + 1, r, rs(p), x); up(p); } inline void mul(ll ql, ll qr, ll l, ll r, ll p, ll x) { if (ql <= l && r <= qr) { mulmod(tr[p], x); mulmod(tg2[p], x); mulmod(tg1[p], x); return; } ll mid = l + r >> 1; if (tg1[p] || tg2[p] != 1) dn(l, r, p); if (ql <= mid) mul(ql, qr, l, mid, ls(p), x); if (qr > mid) mul(ql, qr, mid + 1, r, rs(p), x); up(p); } inline ll qry(ll ql, ll qr, ll l, ll r, ll p) { if (ql <= l && r <= qr) return tr[p]%mod; ll res = 0; if (tg1[p] || tg2[p] != 1) dn(l, r, p); ll mid = l + r >> 1; if (ql <= mid) addmod(res, qry(ql, qr, l, mid, ls(p))); if (qr > mid) addmod(res, qry(ql, qr, mid + 1, r, rs(p))); return res%mod; } } // namespace seg
  • 相关阅读:
    字符串匹配常见算法(BF,RK,KMP,BM,Sunday)
    JSP基本语法总结【1】(jsp工作原理,脚本元素,指令元素,动作元素)
    JUnit【1】断言用法之assertEquals/True/False/ArrayEquals
    软件测试基础配置
    前端入门20-JavaScript进阶之异步回调的执行时机
    前端入门19-JavaScript进阶之闭包
    前端入门18-JavaScript进阶之作用域链
    前端入门17-JavaScript进阶之作用域
    前端入门16-JavaScript进阶之EC和VO
    前端入门15-JavaScript进阶之原型链
  • 原文地址:https://www.cnblogs.com/planche/p/9333350.html
Copyright © 2011-2022 走看看