hdu 3966 Aragorn's Story

2023-11-17

Problem

acm.hdu.edu.cn/showproblem.php?pid=3966

Reference

树链剖分
树链剖分原理
树链剖分详解及模板
HDU3966(树链剖分)

Meaning

一棵 n 个点的树,每给结点有个值。三种操作:

  • I C1 C2 K:将从结点 C1 到 结点 C2 的路径上的所有点的值都加上 k
  • D C1 C2 K:将从结点 C1 到 结点 C2 的路径上的所有点的值都减去 k
  • Q C:讯问结点 C 的值

Analysis

树链剖分模板。

Notes

树链剖分,是把一棵树拆成若干条链,一条链就可以理解成一个区间。把这些链依次铺在线段树(或其它数据结构)上,那维护树上的信息就转化成维护区间信息。
把树剖成链有很多种剖法,分轻重链地剖(即第一个参考链接中的“启发式剖分”)比较优。

一些概念

size(t)为以节点 t 为根的子树的大小。
对任意一个非叶子节点,在它所有的子节点中,称size最大的那个为重儿子,其它的叫轻儿子
节点与它的重儿子的那条连边称为重边,其它边称为轻边
从某个结点开始,一路沿着重边拓展到叶子,就得到一条链,称为重链,链上的边全都是重边。(好像没有叫轻边的?)
那种启发式剖分就是从树根开始,先一路沿重边拓展下去,得到重链,对于轻儿子,则分别以它们为新的链头,又沿着它的重边拓展,又得到重链。递归这个过程,直到剖完整棵树。

几个数组

  • depth[]:节点的深度
  • tsz[]:以 i 为根的子数的大小
  • fa[]:节点的父节点
  • son[]:节点的重儿子,i 与 son[i] 的连边就是重边
  • top[]:节点所在重链的链头节点
  • pos[]:节点在剖分后的新编号,其实就是把链放进线段树后,这个节点对应线段树的第几片叶子
  • tid[]:跟pos[]是反映射,表示线段树第 i 片叶子对应到原树上的哪一个节点

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 50000, M = N;

int head[N+1], to[M<<1], nxt[M<<1];

void add_edge(int f, int t, int id)
{
    to[id] = t;
    nxt[id] = head[f];
    head[f] = id;
}

/*--- 树链剖分部分 ---*/
int dep[N+1], tsz[N+1], fa[N+1], son[N+1];

// 分轻、重链
void heavy_light(int v, int f, int d)
{
    dep[v] = d;
    tsz[v] = 1;
    fa[v] = f;
    for(int i = head[v]; ~i; i = nxt[i])
        if(to[i] != f)
        {
            heavy_light(to[i], v, d + 1);
            tsz[v] += tsz[to[i]];
            if(son[v] == -1 || tsz[to[i]] > tsz[son[v]])
                son[v] = to[i];
        }
}

int top[N+1], pos[N+1], tid[N+1], tm;

// 剖分
void decompose(int v, int tp)
{
    top[v] = tp;
    pos[v] = ++tm;
    tid[pos[v]] = v;
    if(son[v] == -1)
        return;
    // 先沿重边拓展
    decompose(son[v], tp);
    // 轻儿子作为新的链头,新开一条重链
    for(int i = head[v]; ~i; i = nxt[i])
        if(to[i] != fa[v] && to[i] != son[v])
            decompose(to[i], to[i]);
}

/*--- 线段树部分 ---*/
int tree[N+1<<2], lazy[N+1<<2];

inline void pushup(int rt)
{
    tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}

void pushdown(int rt, int len)
{
    if(lazy[rt])
    {
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        tree[rt<<1] += (len + 1 >> 1) * lazy[rt];
        tree[rt<<1|1] += (len >> 1) * lazy[rt];
        lazy[rt] = 0;
    }
}

void update(int ul, int ur, int v, int l, int r, int id)
{
    if(ul <= l && r <= ur)
    {
        lazy[id] += v;
        tree[id] += (r - l + 1) * v;
        return;
    }
    pushdown(id, r - l + 1);
    int m = l + r >> 1;
    if(ul <= m)
        update(ul, ur, v, l, m, id<<1);
    if(ur > m)
        update(ul, ur, v, m+1, r, id<<1|1);
    pushup(id);
}

void change(int l, int r, int v, int n)
{
    // 不在同一条重链
    while(top[l] != top[r])
    {
        // 用 l 表示链头比较低的那条链
        if(dep[top[l]] < dep[top[r]])
            swap(l, r);
        update(pos[top[l]], pos[l], v, 1, n, 1);
        l = fa[top[l]];
    }
    // 此时已经处于同一条链
    if(dep[l] > dep[r])
        swap(l, r);
    update(pos[l], pos[r], v, 1, n, 1);
}

int query(int p, int l, int r, int id)
{
    if(l == r)
        return tree[id];
    pushdown(id, r - l + 1);
    int m = l + r >> 1, res;
    if(p > m)
        res = query(p, m+1, r, id<<1|1);
    else
        res =  query(p, l, m, id<<1);
    pushup(id);
    return res;
}

int a[N+1];

void build(int l, int r, int id)
{
    lazy[id] = 0;
    if(l == r)
    {
        tree[id] = a[tid[l]];
        return;
    }
    int m = l + r >> 1;
    build(l, m, id<<1);
    build(m+1, r, id<<1|1);
    pushup(id);
}

int main()
{
    int n, m, p;
    while(~scanf("%d%d%d", &n, &m, &p))
    {
        for(int i = 1; i <= n; ++i)
            scanf("%d", a+i);
        memset(head, -1, sizeof head);
        for(int i = 0, f, t, sz = 0; i < m; ++i)
        {
            scanf("%d%d", &f, &t);
            add_edge(f, t, sz++);
            add_edge(t, f, sz++);
        }
        memset(son, -1, sizeof son);
        heavy_light(1, 0, 1);
        tm = 0;
        decompose(1, 1);
        build(1, n, 1);
        for(int q, x, y, k; p--; )
        {
            char op;
            scanf(" %c", &op);
            if(op == 'Q')
            {
                scanf("%d", &q);
                printf("%d\n", query(pos[q], 1, n, 1));
            }
            else
            {
                scanf("%d%d%d", &x, &y, &k);
                if(op == 'D')
                    k = -k;
                change(x, y, k, n);
            }
        }
    }
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

hdu 3966 Aragorn's Story 的相关文章

  • umi学习总结

    文章目录 umi介绍 umi是什么 umi的特性 开发环境 Node js 依赖管理工具 目录结构 路由 配置路由 页面跳转 Link组件 路由组件参数 路由动态参数 query信息 样式 使用css样式 dva 为什么需要状态管理 umi
  • Qt弹出窗口

    Qt弹出Widget窗口置顶 1 需求 Widget每次都弹出且为非模态窗口 2 老版代码 if widget NULL widget new QWidget widget gt show 想象 弹出窗口后 如果发生窗口切换 再次点击时 弹

随机推荐

  • Go语言常用的标准库

    文章目录 打印日志 系统调用命令 json的序列化和反序列化 base64 压缩和解压 标准输入 文件操作 目录操作 init函数 包的可见性 数学库 生成随机数 时间函数 打印日志 package main import log os f
  • Java内存回收机制

    C C 等语言中 内存的分配和释放由程序代码来完成 容易出现由于程序员漏写内存释放代码引起的内存泄露 最终导致系统内存耗尽 Java代码运行在JVM中 由JVM来管理 堆Heap 内存的分配和回收 Garbage Collection 把程
  • 接口如何处理重复请求?

    本文主要来源于 处理重复请求的三种方式 服务端如何高效的处理重复请求 对其整理和总结 用于学习记录 重复请求常用的处理方式就是幂等性处理 幂等性可以理解为 无论执行了多少次重复请求 数据只会处理一次 在数据库里也只会有一条数据 和数据库的唯
  • 以太坊智能合约各方法对应的签名编码

    erc20智能合约常见方法对应的签名编码 常见例如交易 transfer address uint256 编码为 web3 sha3 transfer address uint256 substring 0 10 gt 0xa9059cbb
  • Solidity合约中Merkle Root验证的一点实践

    背景 在上一篇文章 Solidity合约中签名验证的一点实践 中提到过 白名单机制一般有两种 除了签名验证的方式外 就是本文讲述的Merkle Root验证的方式 主要做法是在服务端对白名单地址列表整体构建Merkle树 计算出树的root
  • 解决Hbase报错java.lang.IllegalStateException: The procedure WAL relies on the ability to hsync for....

    完整报错为 java lang IllegalStateException The procedure WAL relies on the ability to hsync for proper operation during compo
  • set的使用

    创建集合 set 1 2 3 4 转化为列表list 1 如果我要在许多列表中找出相同的项 那么用集合是最好不过的了 用集合只用一行就可以解决 x y z 交集 2 去重 gt gt gt lst 1 2 3 4 1 gt gt gt pr
  • 毕业那天我们一起失恋

    毕业那天我们一起失恋 原载 婚姻家庭 VOL 1大四快开学了 我提前了几天来学校 俗话说 磨刀不误砍柴功 我提早来学校 把床铺好 把蚊帐挂起来 把厕所弄干净 把寝室打扫一下 寝室里只有我做这种打扫的事情 寝室有三个人 我一个 丸子一个 还有
  • 【翻译】对计算机未来的10个预测或;我们的首席科学家的无稽之谈

    TLDR WASM将无处不在 编译目标 部署目标 物联网 插件生态系统 这已经在发生了 1 5年 Rust将继续流行 根据RedMonk的指数 在未来几年将超过Go 2 4年 将出现一个严重的Kubernetes的对手 如果它使用WASM并
  • 写个爬虫吧

    import requests url https image baidu com search acjson tn resultjson com ipn rj ct 201326592 is 0 2C0 fp detail logid 1
  • 03-MySQL数据类型

    一 数值类型 整数 MySQL 主要提供的整数类型有 TINYINT SMALLINT MEDIUMINT INT BIGINT 浮点数 浮点类型有两种 分别是单精度浮点数 FLOAT 和双精度浮点数 DOUBLE 定点类型 只有一种 就是
  • 记录一次 JS 解密去混淆的经历 -- 如何破解加密的 JS 代码(一)

    写在前头 昨天发了一个 某JS最牛加密脱壳解密破解去混淆工具 有朋友说上代码不如讲一下思路 于是今天准备捋一下这个思路 顺便当整理复习了 需要直接解密代码的请看上一篇文章 这里只有思路与过程 阅读此文默认你有一定的 JavaScript 基
  • vscode工作区同时显示多个文件

    有时候安装的vscode打开一个文件又打开另一个文件只会保存新的文件 旧的文件别替换 这样做项目比较难受 所以用下面方法可以打开多个文件 workbench editor showTabs true
  • 【E2E】Tesseract5+VS2017+win10源码编译攻略

    一 记录我目前在win10 X64和VS2017的环境下成功编译Tesseract5 0的方式 二 记录在VS2017 C 工程中调用Tesseract4 0的方法 三 记录编译和调用Tesseract4 0过程中踩到的坑和相应的解决方案或
  • IMU立大功:有效减小建筑工人高空坠落风险

    尽管建筑行业不断努力改善工作场所安全 但它仍然是全球最危险的行业之一 建筑行业的工作死亡或致命工伤比例为25 在这些致命伤害中 大约36 是由高空坠落造成的 这是建筑行业从业者意外死亡的主要原因之一 其他国家 包括澳大利亚 中国和韩国 也因
  • 使用eclipse创建JAVA项目

    打开eclipse软件 选择好工作区域 就是项目的储存地址 后登陆 File New Project 选择 Java Project 输入项目名称 点击Finish SRC是专门放java源代码的文件夹 就是你在IDE里编写的各个java类
  • C语言上机实验思路分享9

    实验项目名称 实验十 C 文件基本操作 实验目的及要求 1 掌握文件和文件指针的概念以及文件的定义方法 2 了解文件打开和关闭的概念和方法 3 掌握有关文件操作的函数 实验内容 方法和步骤 1 文件 stu info1 txt 包含学生的基
  • 模板型模板参数报错,无法调试通过,---《深入实践c++模板》例子

    include
  • 迪杰斯特拉算法求图的某个顶点到其他顶点的最短路径问题

    迪杰斯特拉算法 使用图的广度优先遍历算法 比如先从G点出发 找到能与G直接连接的顶点 然后才从与G最近的A出发 找到与A相邻的节点 通过比较G到每个顶点的距离大小 筛选出到每个点的最短路径 代码 迪杰斯特拉算法球最短路径问题 public
  • hdu 3966 Aragorn's Story

    Problem acm hdu edu cn showproblem php pid 3966 Reference 树链剖分 树链剖分原理 树链剖分详解及模板 HDU3966 树链剖分 Meaning 一棵 n 个点的树 每给结点有个值 三