luogu P3384 【模板】重链剖分
参考博客:https://www.cnblogs.com/ivanovcraft/p/9019090.html
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int maxn=1e5+;
struct edge
{
int next,to;
} e[maxn*];
int head[maxn],cnt;
struct node
{
int l,r,ls,rs,sum,lazy;
} a[maxn*];
int n,m,r,rt,mod;
int v[maxn];
int f[maxn];//f[u] 保存结点u的父亲节点
int d[maxn];//d[u] 保存结点u的深度值
int son[maxn];//son[u] 保存重儿子
int size[maxn];//size[u] 保存以u为根的子树节点个数
int top[maxn];//top[u] 保存当前节点所在链的顶端节点
int id[maxn];//id[u] 保存树中每个节点剖分以后的新编号(DFS的执行顺序)
int rk[maxn];//rk[u] 保存当前dfs标号在树中所对应的节点
void add(int x,int y)
{
e[++cnt].next=head[x];
e[cnt].to=y;
head[x]=cnt;
}
void dfs1(int x) //当前节点
{
size[x]=;
d[x]=d[f[x]]+;
for(int v,i=head[x]; i; i=e[i].next)
if((v=e[i].to)!=f[x])
{
f[v]=x;
dfs1(v);
size[x]+=size[v];//子节点的size已被处理,用它来更新父节点的size
if(size[son[x]]<size[v])
son[x]=v; //选取size最大的作为重儿子
}
}
void dfs2(int x,int tp)//当前节点、重链顶端
{
top[x]=tp;
id[x]=++cnt;//标记dfs序
rk[cnt]=x;//序号cnt对应节点u
if(son[x])
dfs2(son[x],tp);
/*我们选择优先进入重儿子来保证一条重链上各个节点dfs序连续,
一个点和它的重儿子处于同一条重链,所以重儿子所在重链的顶端还是t*/
for(int v,i=head[x]; i; i=e[i].next)
if((v=e[i].to)!=f[x]&&v!=son[x])
dfs2(v,v);//一个点位于轻链底端,那么它的top必然是它本身
}
inline void pushup(int x)
{
a[x].sum=(a[a[x].ls].sum+a[a[x].rs].sum)%mod;
}
void build(int l,int r,int x)
{
if(l==r)
{
a[x].sum=v[rk[l]],a[x].l=a[x].r=l;
return;
}
int mid=l+r>>;
a[x].ls=cnt++,a[x].rs=cnt++;
build(l,mid,a[x].ls),build(mid+,r,a[x].rs);
a[x].l=a[a[x].ls].l,a[x].r=a[a[x].rs].r;
pushup(x);
}
inline int len(int x)
{
return a[x].r-a[x].l+;
}
inline void pushdown(int x)
{
if(a[x].lazy)
{
int ls=a[x].ls,rs=a[x].rs,lz=a[x].lazy;
(a[ls].lazy+=lz)%=mod,(a[rs].lazy+=lz)%=mod;
(a[ls].sum+=lz*len(ls))%=mod,(a[rs].sum+=lz*len(rs))%=mod;
a[x].lazy=;
}
}
void update(int l,int r,int c,int x)
{
if(a[x].l>=l&&a[x].r<=r)
{
(a[x].lazy+=c)%=mod,(a[x].sum+=len(x)*c)%=mod;
return;
}
pushdown(x);
int mid=a[x].l+a[x].r>>;
if(mid>=l)
update(l,r,c,a[x].ls);
if(mid<r)
update(l,r,c,a[x].rs);
pushup(x);
}
int query(int l,int r,int x)
{
if(a[x].l>=l&&a[x].r<=r)
return a[x].sum;
pushdown(x);
int mid=a[x].l+a[x].r>>,tot=;
if(mid>=l)
tot+=query(l,r,a[x].ls);
if(mid<r)
tot+=query(l,r,a[x].rs);
return tot%mod;
}
inline int sum(int x,int y)
{
int ret=;
while(top[x]!=top[y])//两点不在同一条重链
{
if(d[top[x]]<d[top[y]])
swap(x,y);
(ret+=query(id[top[x]],id[x],rt))%=mod;//线段树区间求和,处理这条重链的贡献
x=f[top[x]];//将x设置成原链头的父亲结点,走轻边,继续循环
}
//循环结束,两点位于同一重链上,但两点不一定为同一点,所以我们还要统计这两点之间的贡献
if(id[x]>id[y])
swap(x,y);
return (ret+query(id[x],id[y],rt))%mod;
}
inline void updates(int x,int y,int c)
{
while(top[x]!=top[y])
{
if(d[top[x]]<d[top[y]])
swap(x,y);
update(id[top[x]],id[x],c,rt);
x=f[top[x]];
}
if(id[x]>id[y])
swap(x,y);
update(id[x],id[y],c,rt);
}
signed main()
{
scanf("%lld%lld%lld%lld",&n,&m,&r,&mod);
for(int i=; i<=n; i++)
scanf("%lld",&v[i]);
for(int x,y,i=; i<n; i++)
{
scanf("%lld%lld",&x,&y);
add(x,y),add(y,x);
}
cnt=,dfs1(r),dfs2(r,r);
cnt=,build(,n,rt=cnt++);
for(int op,x,y,k,i=; i<=m; i++)
{
scanf("%lld",&op);
if(op==)//将树从 xx 到 yy 结点最短路径上所有节点的值都加上 zz
{
scanf("%lld%lld%lld",&x,&y,&k);
updates(x,y,k);
}
else if(op==)//求树从 xx 到 yy 结点最短路径上所有节点的值之和。
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",sum(x,y));
}
else if(op==)//将以 xx 为根节点的子树内所有节点值都加上 zz
{
scanf("%lld%lld",&x,&y);
update(id[x],id[x]+size[x]-,y,rt);
}
else//以 xx 为根节点的子树内所有节点值之和
{
scanf("%lld",&x);
printf("%lld\n",query(id[x],id[x]+size[x]-,rt));
}
}
return ;
}
luogu P3384 【模板】重链剖分的更多相关文章
- [luogu P3384] [模板]树链剖分
[luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...
- 【luogu P3384 树链剖分】 模板
题目链接:https://www.luogu.org/problemnew/show/P3384 诶又给自己留了个坑..不想写线段树一大理由之前的模板变量名太长 #include <cstdio ...
- Luogu - P3384 树链剖分 [挂模板专用]
题意:请码个树剖模板支持子树区间加/查询和路径加/查询 纯练手 盲敲技能++ 以后网络赛复制模板速度++++ 对链操作时注意方向 #include<bits/stdc++.h> #defi ...
- 树链剖分 (求LCA,第K祖先,轻重链剖分、长链剖分)
2020/4/30 15:55 树链剖分是一种十分实用的树的方法,用来处理LCA等祖先问题,以及对一棵树上的节点进行批量修改.权值和查询等有奇效. So, what is 树链剖分? 可以简单 ...
- 洛谷 - P2146 - 软件包管理器 - 重链剖分
https://www.luogu.org/problem/P2146 继续重链剖分. 这里好像很好懂,每次安装软件就区间改值赋值整个路径是1,然后比较前后的sum值变化就可以了.事实上后一次的sum ...
- Luogu P2742 模板-二维凸包
Luogu P2742 模板-二维凸包 之前写的实在是太蠢了.于是重新写了一个. 用 \(Graham\) 算法求凸包. 注意两个向量 \(a\times b>0\) 的意义是 \(b\) 在 ...
- luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)
luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include< ...
- Luogu P3384 【【模板】树链剖分】
转载请注明出处,部分内容引自banananana大神的博客 ~~别说你不知道什么是树~~╮(─▽─)╭(帮你百度一下) 先来回顾两个问题:1,将树从x到y结点最短路径上所有节点的值都加上z 这也是个模 ...
- 【模板】树链剖分(Luogu P3384)
题目描述 众所周知 树链剖分是个好东西QWQ 也是一个代码量破百的算法 基本定义 树路径信息维护算法. 将一棵树划分成若干条链,用数据结构去维护每条链,复杂度为O(logN). 其实本质是一些数据结 ...
随机推荐
- jenkins 与 gitlab 的持续集成
前言介绍 gitlab与jenkins的安装部署请参考之前的文章:这里介绍一下jenkins与gitlab结合的好处. gitlab可以自己实现CICD功能,jenkins也可以结合其他工具来实现CI ...
- java线程池及创建多少线程合适
java线程池 1.以下是ThreadPoolExecutor参数完备构造方法: public ThreadPoolExecutor(int corePoolSize,int maximumPoolS ...
- LeetCode 304. Range Sum Query 2D - Immutable 二维区域和检索 - 矩阵不可变(C++/Java)
题目: Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper ...
- std::wstring_convert处理UTF8
扔掉MultiByteToWideChar 吧,使用std::wstring_convert和 std::codecvt_utf8 来处理UTF8与WChar之间的互转. VC和Clang都支持哦~ ...
- Java原子变量类需要注意的问题
在学习多线程时,遇到了原子变量类,它是基于 CAS 和 volatile 实现的,能够保障对共享变量进行 read-modify-write 更新操作的原子性和可见性.于是我就写了一段代码试试,自认为 ...
- 浅谈ActionResult之FileResult
FileResult是一个基于文件的ActionResult,利用FileResult,我们可以很容易的将某个物理文件的内容响应给客户端,ASP.NET MVC定义了三个具体的FileResult,分 ...
- Hadoop fs 使用方法
hdfs的基本命令 hdfs dfs -help 查看帮助 在HDFS的文件系统中,HDFS只支持绝对路径 1.-ls: 显示目录信息 hadoop fs -ls / 列出指定目录下的内容 2. ...
- php面试笔记(4)-php基础知识-流程控制
本文是根据慕课网Jason老师的课程进行的PHP面试知识点总结和升华,如有侵权请联系我进行删除,email:guoyugygy@163.com 在面试中,考官往往喜欢基础扎实的面试者,而流程控制相关的 ...
- 如何用Python实现do...while语句
我在编程的时候可能会遇到如下代码: a = 0 while a != 0: a = input() print a 我所设想的运行过程是这样的: 很显然我是想先运行后判断的模式,即 do...whil ...
- Kubernetes 部署 Nginx Ingress Controller 之 nginxinc/kubernetes-ingress
更新:这里用的是 nginxinc/kubernetes-ingress ,还有个 kubernetes/ingress-nginx ,它们的区别见 Differences Between nginx ...