AcWing 2568:树链剖分 ← 线段树+DFS

news2024/11/25 15:31:10

【题目来源】
https://www.acwing.com/problem/content/2570/

【题目描述】
给定一棵树,树中包含 n 个节点(编号 1∼n),其中第 i 个节点的权值为 ai。
初始时,1 号节点为树的根节点。
现在要对该树进行 m 次操作,操作分为以下 4 种类型:
● 1 u v k,修改路径上节点权值,将节点 u 和节点 v 之间路径上的所有节点(包括这两个节点)的权值增加 k。
● 2 u k,修改子树上节点权值,将以节点 u 为根的子树上的所有节点的权值增加 k。
● 3 u v,询问路径,询问节点 u 和节点 v 之间路径上的所有节点(包括这两个节点)的权值和。
● 4 u,询问子树,询问以节点 u 为根的子树上的所有节点的权值和。

【输入格式】
第一行包含一个整数 n,表示节点个数。
第二行包含 n 个整数,其中第 i 个整数表示 ai。
接下来 n−1 行,每行包含两个整数 x,y,表示节点 x 和节点 y 之间存在一条边。
再一行包含一个整数 m,表示操作次数。
接下来 m 行,每行包含一个操作,格式如题目所述。

【输出格式】
对于每个操作 3 和操作 4,输出一行一个整数表示答案。

【数据范围】
1≤n,m≤10^5,
0≤ai,k≤10^5,
1≤u,v,x,y≤n

【输入样例】
5
1 3 7 4 5
1 3
1 4
1 5
2 3
5
1 3 4 3
3 5 4
1 3 5 10
2 3 5
4 1

【输出样例】
16
69

【算法分析】
● 树链剖分
(1)树链剖分的核心思想,就是将树中任意一条路径拆分成 O(log_2 n) 段的
连续区间(或重链)。拆分后,树的所有操作都可转化为区间操作,且通常采用线段树进行维护。
(2)树链剖分的几个概念

重儿子/轻儿子:结点个数最多的子树的根结点称为当前结点的重儿子,其他子结点称为当前结点的轻儿子。若当前结点存在多个结点个数相同的子树,则任选一个子树的根结点作为当前结点的重儿子。故易知每个结点的重儿子是唯一的
重边/轻边:重儿子与父结点之间的边,称为重边。其他边称为轻边
重链:重边构成的极大路径,称为重链。
DFS序:深度优先遍历树的重儿子,可保证树中各条重链结点的编号是连续的。此性质保证了树链剖分后各区间是连续的。
(3)将一条路径拆分为重链的过程,类似于求最近公共祖先(LCA)。

【算法代码】

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int maxn=1e5+5;
const int maxm=maxn<<1;
int val[maxn],e[maxm],ne[maxm],h[maxn],idx;

//id[]:dfn sequence number of the node
//nv[]:point weight of each dfs sequence number
int id[maxn],nv[maxn],tot;

//cnt[]:number of child nodes, top[]:vertex of heavy chain
//son[]:heavy son, fa[]:parent node
int dep[maxn],cnt[maxn],top[maxn],fa[maxn],son[maxn];

int n,m;

struct SegmentTree {
    int le,ri;
    LL sum,lazy;
} tr[maxn<<2];

void add(int a,int b) {
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int father,int depth) { //dfs1:pretreatment
    dep[u]=depth,fa[u]=father,cnt[u]=1;
    for(int i=h[u]; i!=-1; i=ne[i]) {
        int j=e[i];
        if(j==father) continue;
        dfs1(j,u,depth+1);
        cnt[u]+=cnt[j];
        if(cnt[son[u]]<cnt[j]) son[u]=j; //heavy son
    }
}

void dfs2(int u,int vx) { //dfs2:split, t:vertex of heavy chain
    id[u]=++tot,nv[tot]=val[u],top[u]=vx;
    if(!son[u]) return; //leaf node
    dfs2(son[u], vx); //Heavy son's heavy chain split

    for(int i=h[u]; i!=-1; i=ne[i]) { //handle light son
        int j=e[i];
        if(j==fa[u] || j==son[u]) continue;
        dfs2(j,j); //The vertex of the light son's heavy chain is himself
    }
}

/*------------ Content of segment tree ------------*/

void pushup(int u) {
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

void pushdown(int u) {
    auto &rt=tr[u], &L=tr[u<<1], &R=tr[u<<1|1];
    if(rt.lazy) {
        L.sum+=rt.lazy*(L.ri-L.le+1);
        L.lazy+=rt.lazy;
        R.sum+=rt.lazy*(R.ri-R.le+1);
        R.lazy+=rt.lazy;
        rt.lazy=0;
    }
}

void build(int u,int le,int ri) {
    tr[u]= {le,ri,nv[ri],0};
    if(le==ri) return;
    int mid=le+ri>>1;
    build(u<<1,le,mid), build(u<<1|1,mid+1,ri);
    pushup(u);
}

void update(int u,int le,int ri,int k) {
    if(le<=tr[u].le && ri>=tr[u].ri) {
        tr[u].lazy+=k;
        tr[u].sum+=k*(tr[u].ri-tr[u].le+1);
        return;
    }
    pushdown(u);
    int mid=tr[u].le+tr[u].ri>>1;
    if(le<=mid) update(u<<1,le,ri,k);
    if(ri>mid) update(u<<1|1,le,ri,k);
    pushup(u);
}

LL query(int u,int le,int ri) {
    if(le<=tr[u].le && ri>=tr[u].ri) return tr[u].sum;
    pushdown(u);
    int mid=(tr[u].le+tr[u].ri)>>1;
    LL res=0;
    if(le<=mid) res+=query(u<<1,le,ri);
    if(ri>mid) res+=query(u<<1|1,le,ri);
    return res;
}

void update_path(int u,int v,int k) {
    while(top[u]!=top[v]) { //Climb up to find the same heavy chain
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        //Because of dfs order, the id of the upper node
        //must be smaller than that of the lower node
        update(1,id[top[u]],id[u],k);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);

    //In the same heavy chain, the remaining intervals are processed
    update(1,id[v],id[u],k);
}

LL query_path(int u,int v) {
    LL res=0;
    while(top[u]!=top[v]) { //Climb up to find the same heavy chain
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        res+=query(1,id[top[u]],id[u]);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);

    //In the same heavy chain, the remaining intervals are processed
    res+=query(1,id[v],id[u]);
    return res;
}

void update_tree(int u,int k) { //Add k to all the subtrees
    //Because of the order of dfs, the interval can be found directly
    //by the number of subtree nodes
    update(1,id[u],id[u]+cnt[u]-1,k);
}

LL query_tree(int u) {
    //Because of the order of dfs, the interval can be found directly
    //by the number of subtree nodes
    return query(1,id[u],id[u]+cnt[u]-1);
}

int main() {
    scanf("%d",&n);
    memset(h,-1,sizeof h);
    for(int i=1; i<=n; i++) scanf("%d",&val[i]);
    for(int i=1; i<n; i++) {
        int a,b;
        scanf("%d %d",&a,&b);
        add(a,b),add(b,a);
    }
    dfs1(1,-1,1);
    dfs2(1,1);
    build(1,1,n);

    scanf("%d",&m);
    while(m--) {
        int op,u,v,k;
        scanf("%d%d",&op,&u);
        if(op==1) {
            scanf("%d%d",&v,&k);
            update_path(u,v,k);
        } else if(op==2) {
            scanf("%d",&k);
            update_tree(u,k);
        } else if(op==3) {
            scanf("%d",&v);
            printf("%lld\n",query_path(u,v));
        } else printf("%lld\n",query_tree(u));
    }

    return 0;
}

/*
in:
5
1 3 7 4 5
1 3
1 4
1 5
2 3
5
1 3 4 3
3 5 4
1 3 5 10
2 3 5
4 1

out:
16
69
*/




【参考文献】
https://www.acwing.com/solution/content/62664/
https://www.acwing.com/problem/content/video/2570/
https://blog.csdn.net/qq_46105170/article/details/125497358








 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1709319.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

yolov10 使用自己的数据集训练目标检测模型

1 环境配置(使用anaconda) conda create -n yolov10 python=3.9 //创建虚拟环境 conda activate yolov10 //激活虚拟环境 pip install -r requirements.txt //执行yolov10 路径下requirements.txt 安装依赖 pip install -e .2.数据集制作 使用lableImage制作数据集(win版…

C# WPF编程基础

XAML文件内容 <Window x:Class"Wpf_demo1.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expression/ble…

【Python】 列表中的删除操作:del、remove 和 pop 的区别

基本原理 在Python中&#xff0c;列表&#xff08;list&#xff09;是一种非常灵活的数据结构&#xff0c;它允许我们存储一系列的元素。在处理列表时&#xff0c;我们经常需要添加、修改或删除元素。在删除元素时&#xff0c;我们可以使用三种不同的方法&#xff1a;del、rem…

SEO之核心关键词(二)

初创企业或者需要建站的朋友看以下两篇文章&#xff0c;谢谢支持&#xff1a; 我给不会敲代码又想搭建网站的人建议新手上云 &#xff08;接上一篇。。。。&#xff09; 4、查询搜索次数 经过自己及朋友、同事的头脑风暴和检查竞争对手网站之后&#xff0c;再到Google 关键词…

跟进2年弄丢1.8亿,你的大客管理错在哪里?

数量并非目的之所在&#xff0c;质量才是根本之道。重视1%的超级用户&#xff0c;才是提高效率的关键所在。 ——凯文凯利 在当今的商业环境中&#xff0c;大客户已成为销售服务型企业最宝贵的资产。他们不仅贡献了企业收入的重要一环&#xff0c;…

韩顺平0基础学Java——第13天

p264-p284 安装IDEA&#xff0c;熟悉一下软件。 尴尬了&#xff0c;难道是这个版本的idea不支持jdk17&#xff0c;难受住了 成功了&#xff0c;顺便跑一下昨天的作业&#xff1a; 这都要跑2秒&#xff1f;是电脑的问题还是谁的问题&#xff1f;控制台里跑的好快的哦 设置id…

vue中在mounted使用$refs获取不到DOM元素

vue中在mounted使用$refs获取不到DOM元素 前言解决方案1、通过使用$nextTick来获取2、updated中获取 前言 在使用ref的时候&#xff0c;在mounted中通过$ref获取节点是获取不到报undefined this.$refs.xx 为 undefined 解决方案 在mounted钩子中加载回来的数据不会在这个阶段更…

淘宝扭蛋机小程序:探索未知,扭出惊喜

在数字时代&#xff0c;我们一直在寻找一种新颖、有趣且充满惊喜的购物方式。淘宝扭蛋机小程序正是为了满足这一需求而诞生的创新之作&#xff0c;它将传统扭蛋机的乐趣与淘宝的丰富商品库完美结合&#xff0c;为您带来前所未有的购物体验。 打破传统&#xff0c;创新玩法 淘…

仅1天录用!3天见刊!这本毕业“水刊”,全科都收,极速上线!

【欧亚科睿学术】 01 期刊简介 【期刊类别】计算机工程类EI 【期刊简介】最新EI期刊目录内源刊 【版面情况】仅10篇版面 【审稿周期】预计2-4周左右录用 【检索情况】EI&Scopus双检 【征稿领域】计算机工程领域相关稿件均可。 02 征稿领域 该期刊发表有关计算机在各…

面试二十六、c++语言级别的多线程编程

一、 多线程编程 ​​​​​ 这里的c语言级别的多线程和linux的有一定的区别&#xff0c;c语言级别提供的多线程比较严格&#xff0c;如果主线程结束了&#xff0c;但是子线程没有结束&#xff0c;进程就会异常终止&#xff0c;而linux不会&#xff0c;会继续执行。 二、模拟卖…

ComfyUI工作流网站

https://openart.ai/home https://comfyworkflows.com/ https://civitai.com/

《java数据结构》--栈的详解

一.栈的认识 栈是一种不同于链表和顺序表的储存数据结构&#xff0c;它对存储数据和取出数据有着特殊的要求&#x1f914;。 首先栈只能从一端存储数据&#xff0c;也就是从一端进&#xff0c;还从这一端出这也是栈最大的特点&#xff0c;这也导致在栈中存取数据都必须遵循先…

【Linux】常用基础命令 | 搭建云服务器优化环境 | 程序的部署

文章目录 Linux常用命令及搭建环境一、LinuxLinux发行版 1.常用命令1.ls2.cd3.pwd4.touch5.cat6.echo7.vim8.mkdir9.rm10.mv11.cp12.man13.grep14.ps15.netstat 2.搭建Java Web程序的运行环境包管理器1.安装JDK2.安装Tomcat3.安装mysql 3.程序的部署 Linux常用命令及搭建环境 …

springcloud-服务拆分与远程调用

一 微服务 1.1简单了解 SpringCloud SpringCloud是目前国内使用最广泛的微服务框架。官网地址&#xff1a;Spring Cloud。 SpringCloud集成了各种微服务功能组件&#xff0c;并基于SpringBoot实现了这些组件的自动装配&#xff0c;从而提供了良好的开箱即用体验&#xff1a…

温故而知新-Spring篇【面试复习】

温故而知新-Spring篇【面试复习】 前言版权推荐温故而知新-Spring篇IOCAOP循环依赖springboot如果要对属性文件中的账号密码加密如何实现&#xff1f;SpringBoot的优点Spring Boot 的核心注解是哪个&#xff1f;它主要由哪几个注解组成的&#xff1f; 最后 前言 2023-7-31 15:…

React18 apexcharts数据可视化之折线图

基础折线图 import ApexChart from react-apexcharts;export function Basic() {// 数据序列const series [{name: "Desktops",data: [10, 41, 35, 51, 49, 62, 69, 91, 148]},]// 图表选项const options {// 图表chart: {height: 650,type: line,zoom: {enabled:…

QT学习(20):QStyle类

Qt包含一组QStyle子类&#xff0c;这些子类&#xff08;QWindowsStyle&#xff0c;QMacStyle等&#xff09;模拟Qt支持的不同平台的样式&#xff0c;默认情况下&#xff0c;这些样式内置在Qt GUI模块中&#xff0c;样式也可以作为插件提供。 Qt的内置widgets使用QStyle来执行几…

基于vuestic-ui实战教程 - 页面篇

1. 简介 前面介绍了基本的内容比如如何获取动态数据&#xff0c;下面就到登录进来后的页面实现了&#xff0c;相信各位读者或多或少都有 element-uijs 的实战经历&#xff0c;那么 vuestic-uits 实现的页面又该如何写呢&#xff1f;带着疑问开启今天的学习&#xff08;声明由于…

【个人博客搭建】(20)获取操作用户信息(IHttpContextAccessor)

IHttpContextAccessor在ASP.NET Core中扮演着至关重要的角色。它为开发者提供了一种方便的方式来访问和操作HttpContext对象&#xff0c;从而允许在整个应用程序中轻松地管理和使用HTTP请求和响应的相关信息。下面将深入探讨IHttpContextAccessor的作用、使用方法以及如何通过它…

Round-Robin 调度逻辑算法

Round-Robin 调度逻辑算法 1 Intro1.1 固定优先级1.2 Round-Robin算法 之前上学还是工作&#xff0c;都接触过调度算法&#xff1a;Round-Robin和weight-Round Robin算法&#xff0c;但只知道它的功能和目的是什么&#xff0c;没有具体了解如何实现的&#xff1b; 现在是工作上…