首页 > 其他分享 >#8. 「模板」树链剖分

#8. 「模板」树链剖分

时间:2024-08-26 10:16:58浏览次数:10  
标签:ll 剖分 dep top 树链 int dfn 节点 模板

题目传送门:#8. 「模板」树链剖分

前置知识

  • 重链:重链(Heavy Path)是指树链剖分中的一条主要的路径,该路径上的节点数量较多,相邻节点之间的距离较近。轻链(Light Path)是指树链剖分中的其他路径,相邻节点之间的距离较远。

  • LCA:最近公共祖先

分析

上树状数组

首先,我们需要定义一个数组vals来存储每个节点的权值。然后,我们可以使用一个数组tree来表示树状数组,数组的下标表示节点的编号。对于节点i,其在tree数组中的下标为i,其父节点在tree数组中的下标为i+(i&-i)

然后只需要实现以下几个操作:

换根:将树的根节点设置为新根节点x。我们可以将所有节点的权值都减去vals[x],然后再将vals[x]加到所有节点的权值中即可。
修改路径权值:给定两个节点u和v,将节点u到节点v的路径上的所有节点权值增加一个给定的值x。我们可以先将节点u的权值增加x,然后再将节点v的权值减去x。最后将tree数组中节点u到节点v的路径上的节点的值都增加x。
修改子树权值:给定一个节点x,将以节点x为根的子树内的所有节点权值增加一个给定的值y。我们可以将节点x的权值增加y,然后将tree数组中节点x及其所有子节点的值都增加y。
询问路径:给定两个节点u和v,询问节点u到节点v的路径上所有节点权值的和。我们可以先计算节点v到根节点的权值和,再计算节点u的权值,最后将两者相减即可。
询问子树:给定一个节点x,询问以节点x为根的子树内所有节点权值的和。我们可以直接返回tree数组中节点x及其所有子节点的值的和。

✿✿ヽ(°▽°)ノ✿

等等,还要上代码

Code

10分

#include <cstdio>
#define reg register
int read() {
    reg int s = 0, f = 1;
    reg char ch;
    for (; (ch = getchar()) < '0' || ch > '9'; ch == '-' ? f = -f : 0)
        ;
    for (; ch >= '0' && ch <= '9'; s = (s << 3) + (s << 1) + ch - 48, ch = getchar())
        ;
    return s * f;
}
const int N = 1e5 + 50;
int next[N], to[N], first[N], tag[N << 2], tree[N << 2], val[N], cnt = 0, tot = 0, n, m;
int dfn[N], son[N], size[N], efn[N], dep[N], top[N], fa[N], root, g[N];
inline void push_up(int now) { tree[now] = tree[now << 1] + tree[now << 1 | 1]; }
inline void add(int now, int l, int r, int v) {
    tree[now] += (r - l + 1) * v;
    tag[now] += v;
}
void push_down(int now, int l, int r) {
    if (tag[now]) {
        int mid = (l + r) >> 1;
        add(now << 1, l, mid, tag[now]);
        add(now << 1 | 1, mid + 1, r, tag[now]);
        tag[now] = 0;
    }
}
void _add(int u, int v) { next[++cnt] = first[u], first[u] = cnt, to[cnt] = v; }
void build(int now, int l, int r) {
    if (l == r) {
        tree[now] = val[g[l]];
        return;
    }
    int mid = (l + r) >> 1;
    build(now << 1, l, mid);
    build(now << 1 | 1, mid + 1, r);
    push_up(now);
}
void mobify(int now, int l, int r, int L, int R, int v) {
    if (L <= l && r <= R) {
        add(now, l, r, v);
        return;
    }
    int mid = (l + r) >> 1;
    push_down(now, l, r);
    if (L <= mid)
        mobify(now << 1, l, mid, L, R, v);
    if (R > mid)
        mobify(now << 1 | 1, mid + 1, r, L, R, v);
    push_up(now);
}
int query(int now, int l, int r, int L, int R) {
    if (L <= l && r <= R)
        return tree[now];
    int mid = (l + r) >> 1, ans = 0;
    push_down(now, l, r);
    if (L <= mid)
        ans += query(now << 1, l, mid, L, R);
    if (R > mid)
        ans += query(now << 1 | 1, mid + 1, r, L, R);
    return ans;
}
void dfs0(int now) {
    son[now] = 0;
    size[now] = 1;
    for (reg int i = first[now]; i; i = next[i])
        if (!dep[to[i]]) {
            fa[to[i]] = now;
            dep[to[i]] = dep[now] + 1;
            dfs0(to[i]);
            size[now] += size[to[i]];
            if (!son[now] || size[to[i]] > size[son[now]])
                son[now] = to[i];
        }
}
void dfs1(int now, int t) {
    dfn[now] = ++tot;
    top[now] = t;
    g[tot] = now;
    if (son[now])
        dfs1(son[now], t);
    for (reg int i = first[now]; i; i = next[i])
        if (!dfn[to[i]])
            dfs1(to[i], to[i]);
    efn[now] = tot;
}
int lca(int u, int v) {
    while (top[u] != top[v]) (dep[top[u]] > dep[top[v]]) ? u = fa[top[u]] : v = fa[top[v]];
    return (dep[u] < dep[v]) ? u : v;
}
void addpath(int u, int v, int k) {
    while (top[u] != top[v])
        (dep[top[u]] > dep[top[v]]) ? (mobify(1, 1, n, dfn[top[u]], dfn[u], k), u = fa[top[u]])
                                    : (mobify(1, 1, n, dfn[top[v]], dfn[v], k), v = fa[top[v]]);
    (dep[u] < dep[v]) ? mobify(1, 1, n, dfn[u], dfn[v], k) : mobify(1, 1, n, dfn[v], dfn[u], k);
}
void addtree(int u, int k) {
    if (u == root) {
        mobify(1, 1, n, 1, n, k);
        return;
    }
    int g = lca(u, root);
    if (g != u)
        mobify(1, 1, n, dfn[u], efn[u], k);  // puts("wtf");
    else {
        // puts("wtm");
        if (dfn[root] > 1)
            mobify(1, 1, n, 1, dfn[root] - 1, k);
        if (efn[root] < n)
            mobify(1, 1, n, efn[root] + 1, n, k);
        // mobify(1,1,n,1,dfn[u]-1,k);
        // mobify(1,1,n,efn[u]+1,n,k);
    }
}
void querypath(int u, int v) {
    int ans = 0;
    while (top[u] != top[v])
        if (dep[top[u]] > dep[top[v]])
            ans += query(1, 1, n, dfn[top[u]], dfn[u]), u = fa[top[u]];
        else
            ans += query(1, 1, n, dfn[top[v]], dfn[v]), v = fa[top[v]];
    // printf("%d %d %d\n",u,v,ans);
    ans += (dep[u] < dep[v]) ? query(1, 1, n, dfn[u], dfn[v]) : query(1, 1, n, dfn[v], dfn[u]);
    printf("%d\n", ans);
}
void querytree(int u) {
    if (u == root) {
        printf("%d\n", tree[1]);
        return;
    }
    int g = lca(u, root);
    if (g != u)
        printf("%d\n", query(1, 1, n, dfn[u], efn[u]));
    else {
        int ans = 0;
        if (dfn[root] > 1)
            ans += query(1, 1, n, 1, dfn[root] - 1);
        if (efn[root] < n)
            ans += query(1, 1, n, efn[root] + 1, n);
        printf("%d\n", ans);
    }
}
int main() {
    n = read();
    root = 1;
    for (reg int i = 1; i <= n; i++) val[i] = read();
    for (reg int u = 2, v; u <= n; u++) v = read(), _add(v, u);
    m = read();
    dep[1] = 1;
    dfs0(1);
    dfs1(1, 1);
    build(1, 1, n);
    for (reg int i = 1, opt, u, v, k; i <= m; i++) {
        opt = read();
        if (opt == 1)
            root = read();
        if (opt == 2)
            u = read(), v = read(), k = read(), addpath(u, v, k);
        if (opt == 3)
            u = read(), k = read(), addtree(u, k);
        if (opt == 4)
            u = read(), v = read(), querypath(u, v);
        if (opt == 5)
            u = read(), querytree(u);
    }
    return 0;
}

100分代码

#include <bits/stdc++.h>
#define INF 0x7fffffff
#define ll long long
using namespace std;

ll read() {
    ll k = 1, x = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            k = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - 48, ch = getchar();
    return k * x;
}

const int N = 3e5 + 10;
struct EDGE {
    ll to, pre;
} edge[N];
ll a[N], size[N], fa[N], dep[N], id[N], w[N], son[N], top[N], head[N];
ll cnt, n, m, r;

//========== 关于换根的操作 =========
inline ll find(ll u, ll v) {  //找从u(题目查询点的)到root第一个儿子
    while (top[u] != top[v]) {
        if (dep[top[u]] > dep[top[v]])
            swap(u, v);  //保证u的深度更浅
        if (fa[top[v]] == u)
            return top[v];  //若链头的父亲恰好为u返回链头
        v = fa[top[v]];     //不断向上跳
    }
    if (dep[u] > dep[v])
        swap(u, v);
    return son[u];
}
inline ll lca(ll u, ll v) {
    while (top[u] != top[v]) {
        if (dep[top[u]] > dep[top[v]])
            swap(u, v);
        v = fa[top[v]];
    }
    return dep[u] >= dep[v] ? v : u;
}
//==================================

//================== 以下为线段树模板 ====================
#define up(x) t[x].sum = t[x << 1].sum + t[x << 1 | 1].sum
struct node {
    ll l, r;
    ll sum, lazy;
} t[N << 2];
ll len(ll p) { return t[p].r - t[p].l + 1; }
void brush(ll p, ll k) {
    t[p].lazy += k;
    t[p].sum += len(p) * k;
}
void push_down(ll p) {
    brush(p << 1, t[p].lazy);
    brush(p << 1 | 1, t[p].lazy);
    t[p].lazy = 0;
}
void build(ll p, ll l, ll r) {
    t[p].l = l;
    t[p].r = r;
    if (l == r) {
        t[p].sum = w[l];
        return;
    }
    ll mid = l + r >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    up(p);
}
void update(ll p, ll l, ll r, ll k) {
    if (t[p].l >= l && t[p].r <= r) {
        brush(p, k);
        return;
    }
    push_down(p);
    ll mid = t[p].l + t[p].r >> 1;
    if (mid >= l)
        update(p << 1, l, r, k);
    if (r >= mid + 1)
        update(p << 1 | 1, l, r, k);
    up(p);
}
ll getans(ll p, ll l, ll r) {
    if (t[p].l >= l && t[p].r <= r)
        return t[p].sum;
    push_down(p);
    ll ans = 0;
    ll mid = t[p].l + t[p].r >> 1;
    if (l <= mid)
        ans += getans(p << 1, l, r);
    if (r >= mid + 1)
        ans += getans(p << 1 | 1, l, r);
    return ans;
}
//================== 以上为线段树模板 ====================

//================== 以下为树剖操作模板 ====================
inline void updatetree(ll s, ll t, ll c) {  // s到t的简单路径加上c
    while (top[s] != top[t]) {              //倍增思想,深度大的往上跳
        if (dep[top[s]] > dep[top[t]])
            swap(s, t);                   //默认t深度大
        update(1, id[top[t]], id[t], c);  //先维护这一段链
        t = fa[top[t]];                   //跳到这个链头的父节点,维护下一个链
    }
    if (dep[s] > dep[t])
        swap(s, t);              //默认t深度大
    update(1, id[s], id[t], c);  //维护s与现在t(同深度)的链
}
inline ll getanstree(ll s, ll t) {  //查询s到t的简单路径权值和
    ll ans = 0;
    while (top[s] != top[t]) {  //倍增思想,深度大的往上跳
        if (dep[top[s]] > dep[top[t]])
            swap(s, t);                       //默认t深度大
        ans += getans(1, id[top[t]], id[t]);  //先查询这一段链
        t = fa[top[t]];                       //跳到这个链头的父节点,查询下一个链
    }
    if (dep[s] > dep[t])
        swap(s, t);                  //默认t深度大
    ans += getans(1, id[s], id[t]);  //查询s与现在t(同深度)的链
    return ans;
}
inline void updateson(ll x, ll t) {  //以x为根的子树加上t
    if (x == r)
        update(1, 1, n, t);  //当 x=root时,x就是此时整棵树的根,那么就是全局修改。
    else {
        ll LCA = lca(x, r);
        if (LCA != x)  //若root不在x的子树中,正常修改
            update(1, id[x], id[x] + size[x] - 1, t);
        else {  //若在子树内,修改它到根的第一个儿子子树的补集
            ll u = find(x, r);
            update(1, 1, n, t);
            update(1, id[u], id[u] + size[u] - 1, -t);
        }
    }
}
inline ll getansson(ll x) {  //以x为根的子树加上t
    if (x == r)
        return getans(1, 1, n);  //当 x=root时,x就是此时整棵树的根,那么就是全局查询。
    else {
        ll LCA = lca(x, r);
        if (LCA != x)  //若root不在x的子树中,正常查询
            return getans(1, id[x], id[x] + size[x] - 1);
        else {  //若在子树内,查询它到根的第一个儿子子树的补集
            ll u = find(x, r);
            return getans(1, 1, n) - getans(1, id[u], id[u] + size[u] - 1);
        }
    }
}
//================== 以上为树剖操作模板 ====================

//================== 以下为dfs ====================
inline void dfs1(ll x, ll fath, ll deep) {  // x当前节点,f父亲,deep深度
    dep[x] = deep;                          //标记每个点的深度
    fa[x] = fath;                           //标记每个点的父亲
    size[x] = 1;                            //标记每个非叶子节点的子树大小
    ll maxson = -1;                         //记录重儿子的儿子数
    for (ll i = head[x]; i; i = edge[i].pre) {
        ll y = edge[i].to;
        if (y == fath)
            continue;
        dfs1(y, x, deep + 1);
        size[x] += size[y];      //把它的儿子数加到它身上
        if (size[y] > maxson) {  //标记每个非叶子节点的重儿子编号
            son[x] = y;
            maxson = size[y];
        }
    }
}

ll tim;
inline void dfs2(ll x, ll fst) {  // x当前节点,fst当前链的顶端
    id[x] = ++tim;                //标记每个点dfs序
    w[tim] = a[x];                //赋每个点的初始值
    top[x] = fst;                 //标记这个点所在链的顶端
    if (!son[x])
        return;         //如果没有儿子则返回
    dfs2(son[x], fst);  //按先处理重儿子
    for (ll i = head[x]; i; i = edge[i].pre) {
        ll y = edge[i].to;
        if (y == fa[x] || y == son[x])
            continue;
        dfs2(y, y);  //轻儿子都有一条从它自己开始的链
    }
}
//================== 以上为dfs ====================

inline void add(ll u, ll v) {  //链式前向星
    edge[++cnt].pre = head[u];
    edge[cnt].to = v;
    head[u] = cnt;
}

int main() {
    n = read();
    r = 1;
    for (ll i = 1; i <= n; i++) a[i] = read();
    for (ll i = 1; i < n; i++) {
        ll a;
        a = read();
        add(a, i + 1), add(i + 1, a);
    }

    dfs1(r, 0, 1);
    dfs2(r, r);
    build(1, 1, n);
    m = read();
    for (ll i = 1; i <= m; i++) {
        ll opt = read(), x = read();
        if (opt == 1)
            r = x;
        if (opt == 2) {
            ll y = read(), z = read();
            updatetree(x, y, z);
        }
        if (opt == 4) {
            ll y = read();
            cout << getanstree(x, y) << endl;
        }
        if (opt == 3) {
            ll z = read();
            updateson(x, z);
        }
        if (opt == 5)
            cout << getansson(x) << endl;
    }
    return 0;
}

标签:ll,剖分,dep,top,树链,int,dfn,节点,模板
From: https://www.cnblogs.com/yhy2013/p/18380155

相关文章

  • Qt+OpenCascade开发笔记(二):Qt引入occ库,搭建工程模板发布Demo
    前言  OpenCASCADE是由OpenCascadeSAS公司开发和支持的开源软件开发平台,旨在为特定领域快速开发程序而设计。它是一个面向对象的C++类库,提供了丰富的几何造型、数据交换和可视化等功能,成为许多CAD软件的核心组件。  本篇描述搭建Qt开发occ环境过程。Demo  注意......
  • html模板之动漫主题《熊出没》 web期末大作业
    一、......
  • 模板
    //模板集合/*class,struct,函数的第1个花括号不断行,if,for,while等花括号单独一行*//*所有private首字母大写,所有public接口全小写*//*变量多个单词不隔开,函数用下划线隔开,构造函数形参可用下划线*//*指针变量p做前缀,指针类型typedef为ptr*/#pragmaGCCoptimi......
  • 自适应seo高仿草民电影网源码 苹果cmsv10模板
    自适应seo高仿草民电影网源码 苹果cmsv10模板源码介绍自适应SEO高仿草民电影网源码是一款基于苹果CMSv10开发的模板,旨在为用户提供一个高度仿真的草民电影网站体验。该模板不仅在视觉设计上模仿了草民电影网的布局和风格,还特别优化了搜索引擎优化(SEO)功能,以提高网站在搜索引......
  • 影视网站模板源码-响应式网页模板-带后台自适应整站源码
    影视网站模板源码-响应式网页模板-带后台自适应整站源码影视网站模板源码源码介绍本源码是一个响应式影视网站模板,适用于搭建电影、电视剧、动漫等视频内容的在线观看平台。模板采用HTML5、CSS3和JavaScript开发,支持自适应布局,能够在不同设备上提供良好的用户体验。后台管理......
  • 重链剖分
    思想我先给出一些定义:定义一个结点的重子节点为其子节点中子树节点数最大的子节点,如有多个,随便取一个作为重儿子,如果没有子节点,就没有重儿子。定义一个结点的轻子节点为其除重子节点外的子节点。从这个节点到重子节点的边为重边,到其他子节点的边为轻边。由重边首位相连的链......
  • 快看过来,毕业设计开题报告万能模板!
    我们给出了一个通用的开题报告模版,同时也填充了内容;大家可以自行根据自己的课题修改xxx学院毕业设计(论文)开题报告课题背景及意义随着高校教学体制和教育方式的改革与发展,高校校园建设面临了新的要求和挑战。大学校园交通系统规划与建设作为校区规划中不可缺少的一部分[1]......
  • 马克斯CMS4.0原创电影模板-自动采集-简洁蓝色模板-带手机wap模板-特色功能一应俱全202
    马克斯CMS4.0原创电影模板-自动采集-简洁蓝色模板-带手机wap模板-特色功能一应俱全2024电影模板马克斯CMS4.0原创电影模板源码介绍马克斯CMS4.0是一款专为电影网站设计的内容管理系统,提供了丰富的功能和灵活的定制选项。该系统支持自动采集功能,能够自动从互联网上抓取最......
  • 【生日视频制作】夜景摩天轮霓虹灯烟花AE模板修改文字软件生成器教程特效素材【AE模板
    摩天轮霓虹灯烟花生日视频制作教程AE模板改文字软件生成器素材怎么如何做的【生日视频制作】夜景摩天轮霓虹灯烟花AE模板修改文字软件生成器教程特效素材【AE模板】生日视频制作步骤:安装AE软件下载AE模板把AE模板导入AE软件修改图片或文字渲染出视频......
  • ZBlog首页与列表页相关模板
    页面公共模板文件模板文件说明header.php公共头部文件footer.php公共尾部文件首页与列表页相关模板模板文件说明index.php首页及列表页主模板文件post-multi.php摘要文章模板post-istop.php置顶文章模板pagebar.php页码模板日志/独......