• 线段树-小总结


    递归版线段树

    中心思想:

    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
  • 相关阅读:
    HDU 4472 Count DP题
    HDU 1878 欧拉回路 图论
    CSUST 1503 ZZ买衣服
    HDU 2085 核反应堆
    HDU 1029 Ignatius and the Princess IV
    UVa 11462 Age Sort
    UVa 11384
    UVa 11210
    LA 3401
    解决学一会儿累了的问题
  • 原文地址:https://www.cnblogs.com/planche/p/9333350.html
Copyright © 2020-2023  润新知