【题解】「HAOI2015」树上操作(述链剖分)

2024-02-08 03:18

本文主要是介绍【题解】「HAOI2015」树上操作(述链剖分),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题面

【题目描述】
有一棵点数为 N N N 的树,以点 1 1 1为根,且树点有边权。然后有 M M M个操作,分为三种:
操作 1 1 1 :把某个节点 x x x 的点权增加 a a a
操作 2 2 2 :把某个节点 x x x 为根的子树中所有点的点权都增加 a a a
操作 3 3 3 :询问某个节点 x x x 到根的路径中所有点的点权和。
【输入】
第一行包含两个整数 N , M N, M N,M 。表示点数和操作数。接下来一行 N N N 个整数,表示树中节点的初始权值。接下来 N − 1 N-1 N1 行每行两个个正整数 f r , t o fr, to fr,to , 表示该树中存在一条边 ( f r , t o ) (fr, to) (fr,to)。再接下来 M M M 行,每行分别表示一次操作。其中
第一个数表示该操作的种类 ( 1 − 3 ) ( 1-3 ) 13 ,之后接这个操作的参数 ( x ( x x 或者 x a ) x a ) xa
【输出】
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
【样例输入】

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

【样例输出】

6
9
13

【数据范围】
对于 100 % 100\% 100% 的数据, N , M ≤ 100000 N,M\leq 100000 N,M100000 ,且所有输入数据的绝对值都不会超过 1 0 6 10^6 106

算法分析

述链剖分模板题目。
对于修改某个节点 x x x 为根的子树,在 D F S DFS DFS序中,子树在连续的相邻的位置,因此只需要知道节点 x x x的位置和子树总的节点数目即可。

参考程序

#include<bits/stdc++.h>
#define N 200100 
#define ll long long 
using namespace std;
int head[N],nxt[N],to[N],tot;
ll w[N];
int n,m;
int father[N],deep[N],size[N];//父节点,深度,子树结点数 
int son[N],top[N];//重儿子,所在重路径的顶部结点(深度最小的结点)
int id[N],rev[N],t;//在线段树中的下标(dfs序),线段树中下标的结点,即rev[id[u]]=u ,dfs序号 
ll sum[2*N],add[2*N]; //线段树 
ll ans_sum;
void Add(int u,int v)
{nxt[++tot]=head[u];to[tot]=v;head[u]=tot;
}
void dfs1(int u,int dad)
{size[u]=1;  //本身结点数为1 father[u]=dad;   deep[u]=deep[dad]+1;for(int i=head[u];i!=-1;i=nxt[i]){int v=to[i];if(v!=dad){dfs1(v,u);size[u]+=size[v];if(size[v]>size[son[u]]) son[u]=v;//找重儿子 }}
}
void dfs2(int u,int dad)
{int v=son[u];if(v)  //优先选择重儿子 {id[v]=++t;//dfs序列 top[v]=top[u];rev[t]=v;dfs2(v,u); }for(int i=head[u];i!=-1;i=nxt[i]){v=to[i];if(!top[v]){id[v]=++t;top[v]=v;        rev[t]=v;dfs2(v,u);}}
}
void built(int k,int l,int r)   //线段树建树
{if(l==r)    {sum[k]=w[rev[l]];return;} int mid=(l+r)/2;built(2*k,l,mid);built(2*k+1,mid+1,r);sum[k]=sum[2*k]+sum[2*k+1];
} 
void pushdown(int k,int l,int r)    //标记下传
{int mid=(l+r)/2;if(add[k]==0) return;add[2*k]+=add[k];sum[2*k]+=(ll)(mid-l+1)*add[k];add[2*k+1]+=add[k];sum[2*k+1]+=(ll)(r-mid)*add[k];add[k]=0;   //下传后标记清零 
}
void modify(int k,int l,int r,int x,int y,ll v)    //区间修改
{if(x>r||y<l) return;if(x<=l&&r<=y){add[k]+=v;sum[k]+=(r-l+1)*v;return;}pushdown(k,l,r); //下传标记int mid=(l+r)/2;if(x<=mid) modify(2*k,l,mid,x,y,v);if(y>=mid+1) modify(2*k+1,mid+1,r,x,y,v);sum[k]=sum[2*k]+sum[2*k+1]; //下传后更新 
}
void query(int k,int l,int r,int x,int y)	//区间询问
{//cout<<l<<" "<<r<<" "<<x<<" "<<y<<endl;if(x>r||y<l) return ;if(x<=l&&r<=y) {ans_sum+=sum[k];					//全部变量ans_sum统计return;}pushdown(k,l,r); //下传标记int mid=(l+r)/2;if(x<=mid) query(2*k,l,mid,x,y);if(y>=mid+1) query(2*k+1,mid+1,r,x,y);
} 
void ask(int u,int v)
{       int fu=top[u],fv=top[v];while(fu!=fv)   //不在同一条重链上  {if(deep[fu]<deep[fv]) {swap(u,v);swap(fu,fv);}   //选择深度大的往上跳query(1,1,n,id[fu],id[u]);  //访问路径 fu->u u=father[fu];fu=top[u]; }if(deep[u]>deep[v]) swap(u,v);   //已经跳到同一条重路径上了 query(1,1,n,id[u],id[v]);   //访问路径 u->v 
}
int main()
{memset(head,-1,sizeof(head));scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%lld",&w[i]);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);Add(x,y);Add(y,x);}dfs1(1,0);id[1]=++t;  //初始化根节点 top[1]=1;rev[1]=1;dfs2(1,0); built(1,1,n); int q;int x;ll y;for(int i=1;i<=m;i++){scanf("%d",&q);  if(q==1)		//单点修改{scanf("%d%lld",&x,&y);modify(1,1,n,id[x],id[x],y);}if(q==2)		//子树修改{scanf("%d%lld",&x,&y);modify(1,1,n,id[x],id[x]+size[x]-1,y);	}if(q==3)		//路径询问{scanf("%d",&x);ans_sum=0;ask(1,x); //路径处理 printf("%lld\n",ans_sum);}}return 0;
}

这篇关于【题解】「HAOI2015」树上操作(述链剖分)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/689754

相关文章

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

C# 读写ini文件操作实现

《C#读写ini文件操作实现》本文主要介绍了C#读写ini文件操作实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录一、INI文件结构二、读取INI文件中的数据在C#应用程序中,常将INI文件作为配置文件,用于存储应用程序的

Python使用qrcode库实现生成二维码的操作指南

《Python使用qrcode库实现生成二维码的操作指南》二维码是一种广泛使用的二维条码,因其高效的数据存储能力和易于扫描的特点,广泛应用于支付、身份验证、营销推广等领域,Pythonqrcode库是... 目录一、安装 python qrcode 库二、基本使用方法1. 生成简单二维码2. 生成带 Log

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

java Stream操作转换方法

《javaStream操作转换方法》文章总结了Java8中流(Stream)API的多种常用方法,包括创建流、过滤、遍历、分组、排序、去重、查找、匹配、转换、归约、打印日志、最大最小值、统计、连接、... 目录流创建1、list 转 map2、filter()过滤3、foreach遍历4、groupingB

Java操作PDF文件实现签订电子合同详细教程

《Java操作PDF文件实现签订电子合同详细教程》:本文主要介绍如何在PDF中加入电子签章与电子签名的过程,包括编写Word文件、生成PDF、为PDF格式做表单、为表单赋值、生成文档以及上传到OB... 目录前言:先看效果:1.编写word文件1.2然后生成PDF格式进行保存1.3我这里是将文件保存到本地后

Python使用Colorama库美化终端输出的操作示例

《Python使用Colorama库美化终端输出的操作示例》在开发命令行工具或调试程序时,我们可能会希望通过颜色来区分重要信息,比如警告、错误、提示等,而Colorama是一个简单易用的Python库... 目录python Colorama 库详解:终端输出美化的神器1. Colorama 是什么?2.

Python视频剪辑合并操作的实现示例

《Python视频剪辑合并操作的实现示例》很多人在创作视频时都需要进行剪辑,本文主要介绍了Python视频剪辑合并操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录介绍安装FFmpegWindowsMACOS安装MoviePy剪切视频合并视频转换视频结论介绍