线段树-小总结

递归版线段树

中心思想:

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
原文地址:https://www.cnblogs.com/planche/p/9333350.html