树链剖分

ps:好久没写博客了QAQ,今天补一次莫名其妙地模板吧。。

树链剖分

  • NOIP联赛经常涉及,虽然没有指名道姓的说用这种算法,可是用了树上差分就很懵逼了...这还不如用树链剖分呢...因为考试要经常涉及,所以我就简单学习了下。(再次鸣谢各路帮助我学习的大佬ymy)

  • 简单理解下,就是可以对树上一段连续区间进行操作,通过轻重链去剖分,要比直接用\(dfs\)序的要好,时间效率也比较高的(虽然不太明白怎么证明的),然后剖成一段段区间,对树上路径进行操作时,直接对区间进行操作。

  • 要想弄这个,首先必须学好线段树和树状数组和各种区间维护利器,这个才能将树链剖分的能量发挥出来。

预处理

  1. 首先\(DfsInit\)这个先处理出\(dep\)\(fa\)\(size\)\(son\)四个数组。

\(fa\) 记的是这个节点的父亲节点。

\(dep\) 为这个点的深度 更新为\(dep[u]=dep[fa[u]]+1\)

\(size\) 为这个节点的子树的大小(需要包括自己),更新为 \(size[u]=1 + \sum _{v} size[v]\),其中\(v\)\(u\)的儿子们。

\(son\) 就是这个节点的重儿子,所谓重就是这个点的\(size\)最大,表达为\(size[son[u]]=\max{(size[v])}\)\(v\)的意义同上。

这个更新过程见程序:

int dep[N], sz[N], son[N], fa[N];
void Dfs_Init(int u, int from) {
    sz[u] = 1;
    dep[u] = dep[from] + 1;
    fa[u] = from;
    for (int i = Head[u]; i; i = Next[i]) {
        int v = to[i];
        if (v == from) continue ;
        Dfs_Init(v, u);
        sz[u] += sz[v];
        if (sz[v] > sz[son[u]] ) son[u] = v;
    }
}

进行剖分

  1. 再用一个\(DfsPart\)再处理出\(top\)\(dfn\)\(num\)三个数组,进行树链剖分。

\(dfn\)就是记这个点的\(dfs\)序,作为这个点在链上的位置,记录方式为\(dfn[u]=++clk\)

\(num\)就是记录\(dfn\)对应的点,即\(num[dfn[u]]=u\),可以求出在链上的点在原来树上的位置

\(top\)就是记这个点所在重链的开始位置,转移为\(top[u]=son[fa[u]]==u?top[fa[u]]:u\),意思为如果这个点是它父亲的重儿子,就继承它父亲的\(top\),否则将自己当成重链的顶端。

程序为:

int top[N], dfn[N], num[N];
void Dfs_Part(int u) {
    static int clk = 0;
    dfn[u] = ++clk;
    num[clk] = u;
    top[u] = son[fa[u]] == u ? top[fa[u]] : u;
    if (son[u]) Dfs_Part(son[u]);
    for (int i = Head[u]; i; i = Next[i]) {
        int v = to[i] ;
        if (v == fa[u] || v == son[u]) continue ;
        Dfs_Part(v);
    }
}

树上操作

  1. 剖完后我们就可以进行树上操作了,主要有两个:

1.对于一个点的子树进行更新和询问;

2.对于任意两个点上的路径进行更新和询问。

第一个操作比较简单,对于\(u\)的子树,我们只要对\([dfn[u],dfn[u]+size[u]-1]\)这一段连续区间进行更新就行了,因为其\(dfs\)序是连续的,可以直接修改。

第二个操作有些复杂,需要跳链操作,简单来说就是两个点不断向上跳,直到它们在同一条链上,在此期间,我们不断向上跳时要更新你需要更新的区间。

跳的过程就是,求\(Lca\),对于两个点的\(x\)\(y\),求出\(top[x]=u\)\(top[y]=v\),比较\(u\)\(v\)的深度,如果\(dep[u]<dep[v]\)就交换一下\(u\)\(v\)\(x\)\(y\),因为我们是要将深度更大的点跳上去,直到他们到同一条链上(即\(top[x]==top[y]\)),中间我们要更新的区间为\([dfn[u],dfn[x]]\)。最后一下我们还要比较一下\(x\)\(y\)的深度,将\(dep\)小的那个放在前面,即如果\(dep[x]>dep[y]\)\(swap\) \(x\)\(y\)再更新\([dfn[x],dfn[y]]\)

代码如下:

void Lca1(int x, int y, int z) {
    z %= Mod;
    while (top[x] != top[y]) {
        int u = top[x], v = top[y];
        if (dep[u] < dep[v]) {
            swap(u, v);
            swap(x, y);
        }
        ul = dfn[u]; ur = dfn[x]; uv = z;
        Update(1, 1, n);
        x = fa[u];
    }
    if (dep[x] > dep[y]) swap(x, y);
    ul = dfn[x]; ur = dfn[y]; uv = z;
    Update(1, 1, n);
}

这个有道很好的裸题去做 Luogu【P3384】【模板】树链剖分 这个就是将两个修改、两个询问结合在一起,比较好做。

简单地附一个代码算了= = 应该还是比较容易看的……

#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * fh;
}

void File() {
#ifdef zjp_shadow
    freopen ("P3384.in", "r", stdin);
    freopen ("P3384.out", "w", stdout);
#endif
}

int n, m, rt, Mod;

const int N = 1e6 + 1e3;
vector<int> G[N];

int dep[N], sz[N], son[N], fa[N];
void Dfs_Init(int u, int from) {
    sz[u] = 1; dep[u] = dep[fa[u] = from] + 1;
    for (int v : G[u]) if (v ^ fa[u]) {
        Dfs_Init(v, u), sz[u] += sz[v];
        if (sz[v] > sz[son[u]]) son[u] = v;
    }
}

int dfn[N], num[N], clk, top[N];
void Dfs_Part(int u) {
    num[dfn[u] = ++ clk] = u;
    top[u] = (son[fa[u]] == u) ? top[fa[u]] : u;
    if (son[u]) Dfs_Part(son[u]);
    for (int v : G[u]) if ((v ^ fa[u]) && (v ^ son[u])) Dfs_Part(v);
}

int val[N];

#define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r
#define Add(o, l, r, v) (sumv[(o)] += 1ll * ((r) - (l) + 1) * (v) % Mod) %= Mod, (tag[(o)] += (v)) %= Mod
struct Segment_Tree {
    int sumv[N << 1], tag[N << 1];

    inline void Push_Down(int o, int l, int r) {
        if (!tag[o]) return ; int ls = o << 1, rs = ls | 1, mid = (l + r) >> 1;
        Add(ls, l, mid, tag[o]); Add(rs, mid + 1, r, tag[o]); tag[o] = 0;
    }

    inline void Push_Up(int o) { sumv[o] = sumv[o << 1] + sumv[o << 1 | 1]; }

    void Update(int o, int l, int r, int ul, int ur, int uv) {
        if (ul <= l && r <= ur) { Add(o, l, r, uv); return ; } int mid = (l + r) >> 1; Push_Down(o, l, r);
        if (ul <= mid) Update(lson, ul, ur, uv); if (ur > mid) Update(rson, ul, ur, uv); Push_Up(o);
    }

    int Query(int o, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr) {
            return sumv[o];
        }
        int mid = (l + r) >> 1, res = 0; Push_Down(o, l, r);
        if (ql <= mid) res += Query(lson, ql, qr);
        if (qr > mid) res += Query(rson, ql, qr); Push_Up(o); return res % Mod;
    }

    void Build(int o, int l, int r) {
        if (l == r) { sumv[o] = val[num[l]]; return ; }
        int mid = (l + r) >> 1; Build(lson); Build(rson); Push_Up(o);
    }
} T;

void Update(int x, int y, int z) {
    while (top[x] ^ top[y]) {
        int u = top[x], v = top[y];
        if (dep[u] < dep[v]) swap(u, v), swap(x, y);
        T.Update(1, 1, n, dfn[u], dfn[x], z); x = fa[u];
    }
    if (dep[x] > dep[y]) swap(x, y);
    T.Update(1, 1, n, dfn[x], dfn[y], z);
}

int Query(int x, int y) {
    int res = 0;
    while (top[x] ^ top[y]) {
        int u = top[x], v = top[y];
        if (dep[u] < dep[v]) swap(u, v), swap(x, y);
        (res += T.Query(1, 1, n, dfn[u], dfn[x])) %= Mod; x = fa[u];
    }
    if (dep[x] > dep[y]) swap(x, y);
    (res += T.Query(1, 1, n, dfn[x], dfn[y])) %= Mod; return res;
}

int main () {
    File();
    n = read(); m = read(); rt = read(); Mod = read();
    For (i, 1, n) val[i] = read();
    For (i, 1, n - 1) {
        int u = read(), v = read();
        G[u].push_back(v);
        G[v].push_back(u);
    }
    Dfs_Init(rt, 0); Dfs_Part(rt); T.Build(1, 1, n);
    For (i, 1, m) {
        int opt = read();
        if (opt == 1) { int x = read(), y = read(), z = read(); Update(x, y, z); }
        else if (opt == 2) { int x = read(), y = read(); printf ("%d\n", Query(x, y)); }
        else if (opt == 3) { int x = read(), z = read(); T.Update(1, 1, n, dfn[x], dfn[x] + sz[x] - 1, z); }
        else if (opt == 4) { int x = read(); printf ("%d\n", T.Query(1, 1, n, dfn[x], dfn[x] + sz[x] - 1)); }
    }
    return 0;
}
  • 一些题目:
    POJ3237
    HDU2801
    SDOI2017

转载于:https://www.cnblogs.com/zjp-shadow/p/7750046.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值