题目链接

//部分转自:https://www.luogu.org/problemnew/solution/P3384

初学树链剖分,感觉这个模板题还是容易理解的,但是实在是码量很大的。

知识点:

  • 重儿子:对于每一个非叶子节点,它的儿子中 儿子数量最多的那一个儿子 为该节点的重儿子
  • 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子
  • 叶子节点没有重儿子也没有轻儿子(因为它没有儿子。。)
  • 重边:连接任意两个重儿子的边叫做重边
  • 轻边:剩下的即为轻边
  • 重链:相邻重边连起来的 连接一条重儿子 的链叫重链
  • 对于叶子节点,若其为轻儿子,则有一条以自己为起点的长度为1的链
  • 每一条重链以轻儿子为起点
  • (太智能了,自己弹出来一张图,

首先是通过两个dfs来将信息进行处理

dfs1()

这个dfs要处理几件事情:

  • 标记每个点的深度dep[]
  • 标记每个点的父亲fa[]
  • 标记每个非叶子节点的子树大小(含它自己)
  • 标记每个非叶子节点的重儿子编号son[]
void dfs1(int u,int pre,int dep)
{f[u]=pre;d[u]=dep;size[u]=1;for(int i=head[u];~i;i=edge[i].p){int v=edge[i].to;if(v==pre) continue;dfs1(v,u,dep+1);size[u]+=size[v];if(size[v]>size[son[u]])son[u]=v;}
}

dfs2()

这个dfs2也要预处理几件事情

  • 标记每个点的新编号
  • 赋值每个点的初始值到新编号上
  • 处理每个点所在链的顶端
  • 处理每条链
void dfs2(int u,int tp)
{top[u]=tp;dfn[u]=++t;tim[t]=u;if(son[u]) dfs2(son[u],tp);for(int i=head[u];~i;i=edge[i].p){int v=edge[i].to;if(v!=f[u]&&v!=son[u]) dfs2(v,v);}
}

顺序:先处理重儿子再处理轻

大概重要的过程就是这两个了,通过轻重儿子划分(先处理重儿子再处理轻)首先可以将其树形结构转化为线性结构,用于线段树来处理各种的树上信息,但是不同于dfs序直接遍历,树链剖分后的新编号,在大多数询问处理的区间中都是连续区间,其实就是把树拆成各个链,然后优雅(baoli)的求解各种问题。

代码:

//#pragma comment (linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
#include<stack>
#include<vector>
#include<map>
#include<queue>
#include<list>
#include<time.h>
#define myself i,l,r
#define lson i<<1
#define rson i<<1|1
#define Lson i<<1,l,mid
#define Rson i<<1|1,mid+1,r
#define half (l+r)/2
#define lowbit(x) x&(-x)
#define min4(a, b, c, d) min(min(a,b),min(c,d))
#define min3(x, y, z) min(min(x,y),z)
#define max3(x, y, z) max(max(x,y),z)
#define max4(a, b, c, d) max(max(a,b),max(c,d))
#define pii make_pair
#define pr pair<int,int>
//freopen("E://1.in","r",stdin);
//freopen("E://1.out","w",stdout);
typedef unsigned long long ull;
typedef long long ll;
const int inff = 0x3f3f3f3f;
const long long inFF = 9223372036854775807;
const int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
const int mdir[8][2] = {0, 1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 1, -1, -1, -1};
const double eps = 1e-10;
const double PI = acos(-1.0);
const double E = 2.718281828459;
using namespace std;
//const int mod=1e9+7;
const int maxn=2e5+5;
int d[maxn];//深度
int son[maxn];//某点的重儿子
int top[maxn];//该点所在链首
int dfn[maxn];//某件点的时间戳
int tim[maxn];//记录某时间戳对应的节点下标
int a[maxn];//节点初始权值
int size[maxn];//以改点为根的子树个数(包括自己
int f[maxn];//前驱
int head[maxn],sign,t;
int n,root,q,mod;
int tree[maxn<<2],lazy[maxn<<2];
struct node
{int to,p;
}edge[maxn<<1];
void init()
{for(int i=0;i<=n;i++) head[i]=-1;sign=t=0;
}
void add(int u,int v)
{edge[sign]=node{v,head[u]};head[u]=sign++;
}
void dfs1(int u,int pre,int dep)
{f[u]=pre;d[u]=dep;size[u]=1;for(int i=head[u];~i;i=edge[i].p){int v=edge[i].to;if(v==pre) continue;dfs1(v,u,dep+1);size[u]+=size[v];if(size[v]>size[son[u]])son[u]=v;}
}
void dfs2(int u,int tp)
{top[u]=tp;dfn[u]=++t;tim[t]=u;if(son[u]) dfs2(son[u],tp);for(int i=head[u];~i;i=edge[i].p){int v=edge[i].to;if(v!=f[u]&&v!=son[u]) dfs2(v,v);}
}
void pushup(int i,int l,int r)
{tree[i]=(tree[lson]+tree[rson])%mod;
}
void pushdown(int i,int l,int r)
{if(lazy[i]){int mid=half;tree[lson]+=(mid-l+1)*lazy[i];tree[rson]+=(r-mid)*lazy[i];lazy[lson]+=lazy[i];lazy[rson]+=lazy[i];lazy[i]=0;}
}
void build(int i,int l,int r)
{if(l==r){tree[i]=a[tim[l]];return;}int mid=half;build(Lson);build(Rson);pushup(myself);
}
void update(int i,int l,int r,int ql,int qr,int val)
{if(ql<=l&&qr>=r){tree[i]+=val*(r-l+1);lazy[i]+=val;return;}int mid=half;pushdown(myself);if(qr<=mid) update(Lson,ql,qr,val);else if(ql>mid) update(Rson,ql,qr,val);else update(Lson,ql,mid,val),update(Rson,mid+1,qr,val);pushup(myself);
}
int query(int i,int l,int r,int ql,int qr)
{if(ql<=l&&qr>=r) return tree[i]%mod;pushdown(myself);int mid=half;if(qr<=mid) return query(Lson,ql,qr);else if(ql>mid) return query(Rson,ql,qr);else return query(Lson,ql,mid)+query(Rson,mid+1,qr);
}
void solve1(int x,int y,int val)//把x,y的简单路径给加上val,解释如下
{while(top[x]!=top[y]){if(d[top[x]]<d[top[y]]) swap(x,y);update(1,1,n,dfn[top[x]],dfn[x],val);x=f[top[x]];}if(d[x]>d[y]) swap(x,y);update(1,1,n,dfn[x],dfn[y],val);
}
int solve2(int x,int y)
{int ans=0;while(top[x]!=top[y])//当两个点不在同一条链上{if(d[top[x]]<d[top[y]]) swap(x,y);//把x定义为深度比较深的点,让他往上边跳ans=(ans+query(1,1,n,dfn[top[x]],dfn[x]))%mod;//ans加上x点到x所在链顶端 这一段区间的点权和x=f[top[x]];//把x跳到x所在链顶端的那个点的上面一个点}if(d[x]>d[y]) swap(x,y);//此时两点就在一条链上了ans=(ans+query(1,1,n,dfn[x],dfn[y]))%mod;//一条链就直接区间和return ans;
}
void solve3(int x,int val)//把该点
{update(1,1,n,dfn[x],dfn[x]+size[x]-1,val);
}
int solve4(int x)
{return query(1,1,n,dfn[x],dfn[x]+size[x]-1)%mod;
}
int main()
{
//    freopen("E://testdata.in","r",stdin);
//    freopen("E://1.out","w",stdout);cin>>n>>q>>root>>mod;init();int x,y,op,val;for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]=a[i]%mod;for(int i=1;i<n;i++)scanf("%d %d",&x,&y),add(x,y),add(y,x);dfs1(root,0,1);dfs2(root,root);build(1,1,n);while(q--){scanf("%d",&op);if(op==1) {scanf("%d %d %d",&x,&y,&val);solve1(x,y,val%mod);}else if(op==2) {scanf("%d %d",&x,&y);printf("%d\n",solve2(x,y));}else if(op==3) {scanf("%d %d",&x,&val);solve3(x,val%mod);}else {scanf("%d",&x);printf("%d\n",solve4(x));}}
}

【模板】树链剖分 P3384相关推荐

  1. 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释...

    P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...

  2. 洛谷P3384 - 树链剖分(树链剖分模板题)

    题目链接 https://www.luogu.org/problemnew/show/P3384 [描述] 树链剖分模板题,记一下板子 #include<bits/stdc++.h> #d ...

  3. 专题·树链剖分【including 洛谷·【模板】树链剖分

    初见安~~~终于学会了树剖~~~ [兴奋]当初机房的大佬在学树剖的时候我反复强调过:"学树剖没有前途的!!!" 恩.真香. 一.重链与重儿子 所谓树剖--树链剖分,就是赋予一个链的 ...

  4. 洛谷3384:【模板】树链剖分——题解

    https://www.luogu.org/problemnew/show/P3384 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 ...

  5. 模板 - 图论 - 树链剖分

    今天来啃一下这个树剖吧. 模板题是要求这四个问题: 将树从x到y结点最短路径上所有节点的值都加上z 求树从x到y结点最短路径上所有节点的值之和 将以x为根节点的子树内所有节点值都加上z 求以x为根节点 ...

  6. 树链剖分概念及模板 + 例题 [POJ3237 tree + 软件包管理器]

    文章目录 概念 模板 例题1:软件包管理器 题目 题解 代码实现 例题2:POJ3237 tree 题目 题解 代码实现 概念 树链剖分主要是用于解决以下这两个问题. 1.更改树上点x到点y的最短路径 ...

  7. 树链剖分 讲解+模板+习题

    今天我们来讲一下树链剖分 树链剖分是什么? 树链剖分是一种用来维护树上路径信息的在线方法,可以处理在线. 通常通过一种方法,将一棵树剖分成若干条链,然后通过数据结构(线段树,BIT等)去维护. 我们通 ...

  8. 【高级数据结构】[SPOJ QTREE]树链剖分/动态树各一模板

    题目: 树链剖分: #include<cstdio> #include<cstring> #include<algorithm> using namespace s ...

  9. 洛谷3384(树链剖分模板题)

    题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...

最新文章

  1. java tomcat 时间不对解决办法
  2. mfc创建一个word操作工程 【转自:http://blog.csdn.net/tg2003/article/details/4399981】
  3. python程序员脱单攻略_作为一只程序员,如何脱单?
  4. ListView和SlidingDrawer
  5. bgp状态idle什么原因_当bgp的邻居状态机处于什么状态是,标志着与邻居的tcp连接已经正常建立...
  6. oracle获取sysdba权限,Oracle 学习笔记: SYSDBA登陆权限问题
  7. P3802 小魔女帕琪
  8. 哇撒!这几个SpringBoot前后端分离项目(附源码),star过千,快去收藏夹吃灰吧。。。
  9. MongoDBTool-最新进展报告
  10. 色彩知识的运用 和 色彩意境解析
  11. 在二叉树中查找两个节点的最近的公共祖先节点(无回溯指针)(NCA--nearest common ancestor)
  12. java osm pbf_OSM PBF 文件格式说明
  13. ROS单线多拨后负载均衡配置
  14. java获取图片像素点的rgb值_java获取图片每个像素点的RGB
  15. conda 包安装位置 虚拟环境_conda指定位置配置虚拟环境
  16. 还没新上市华为鸿蒙os,搭载华为操作系统的新机或年内上市 华为自研操作系统是鸿蒙还是OS?...
  17. 知名软件 XMind 竟然请求其它网站发布其盗版软件
  18. 西南联大数字复原项目获国际论坛奖项 百度AI赋予历史温度
  19. A/D转换器(ADC)
  20. u盘与计算机识别不正常,U盘识别不了的原因及解决方法

热门文章

  1. android studio 通过界面快速查看md5
  2. dp cf 20190615
  3. 【学习参考】Animate.css动画演示
  4. apue读书笔记-第十二章
  5. 用Windows Media Player截图的方法
  6. C++ static
  7. xp启动java设置_Windows XP快速启动经典六招
  8. 小程序picker_小程序·云开发实战 - 迷你微博
  9. no判断 python yes_python的判断 - osc_kewb5r6m的个人空间 - OSCHINA - 中文开源技术交流社区...
  10. 野火开发版屏幕_鸿蒙OS手机版正式发布!副总裁现场表态:我们已经准备好了...