参考博客: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 【模板】重链剖分的更多相关文章

  1. [luogu P3384] [模板]树链剖分

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

  2. 【luogu P3384 树链剖分】 模板

    题目链接:https://www.luogu.org/problemnew/show/P3384 诶又给自己留了个坑..不想写线段树一大理由之前的模板变量名太长 #include <cstdio ...

  3. Luogu - P3384 树链剖分 [挂模板专用]

    题意:请码个树剖模板支持子树区间加/查询和路径加/查询 纯练手 盲敲技能++ 以后网络赛复制模板速度++++ 对链操作时注意方向 #include<bits/stdc++.h> #defi ...

  4. 树链剖分 (求LCA,第K祖先,轻重链剖分、长链剖分)

      2020/4/30   15:55 树链剖分是一种十分实用的树的方法,用来处理LCA等祖先问题,以及对一棵树上的节点进行批量修改.权值和查询等有奇效. So, what is 树链剖分? 可以简单 ...

  5. 洛谷 - P2146 - 软件包管理器 - 重链剖分

    https://www.luogu.org/problem/P2146 继续重链剖分. 这里好像很好懂,每次安装软件就区间改值赋值整个路径是1,然后比较前后的sum值变化就可以了.事实上后一次的sum ...

  6. Luogu P2742 模板-二维凸包

    Luogu P2742 模板-二维凸包 之前写的实在是太蠢了.于是重新写了一个. 用 \(Graham\) 算法求凸包. 注意两个向量 \(a\times b>0\) 的意义是 \(b\) 在 ...

  7. luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

    luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include< ...

  8. Luogu P3384 【【模板】树链剖分】

    转载请注明出处,部分内容引自banananana大神的博客 ~~别说你不知道什么是树~~╮(─▽─)╭(帮你百度一下) 先来回顾两个问题:1,将树从x到y结点最短路径上所有节点的值都加上z 这也是个模 ...

  9. 【模板】树链剖分(Luogu P3384)

    题目描述 众所周知 树链剖分是个好东西QWQ 也是一个代码量破百的算法 基本定义 树路径信息维护算法. ž将一棵树划分成若干条链,用数据结构去维护每条链,复杂度为O(logN). 其实本质是一些数据结 ...

随机推荐

  1. Linux守护进程之systemd

    介绍 历史上,Linux 的启动一直采用init进程:下面的命令用来启动服务. $ sudo /etc/init.d/apache2 start # 或者 $ service apache2 star ...

  2. Shell重定向与exec

    前言 首先我们需要知道exec是什么,exec是linux下面一个创建进程的方法,详情请参考:https://www.cnblogs.com/guge-94/p/11016176.html 重定向 概 ...

  3. 多线程之CountDownLatch的用法及原理笔记

    前言-CountDownLatch是什么? CountDownLatch是具有synchronized机制的一个工具,目的是让一个或者多个线程等待,直到其他线程的一系列操作完成. CountDownL ...

  4. 微信小程序如何创建云函数并安装wx-server-sdk依赖

    时间:2020/01/23 步骤 1.在微信开发者工具中云函数所在的文件夹的图标与其他文件夹是不同的,如下(第一个是云函数): 如果需要使一个普通文件变为云函数文件夹,需要在project.confi ...

  5. Brokers类型配置

    模块 配置项 作用域 备注 DynamicConnectionQuota max.connectionsmax.connections.per.ipmax.connections.per.ip.ove ...

  6. 面试总结 | Linux后台开发不得不看的知识点(给进军bat的你!)

    目录 一 自我介绍 二 面试情况 三 相关知识点汇总 1 c/c++相关 2 计算机网络 3 数据结构相关 4 数据库相关 5 操作系统 6 Linux基础知识及应用编程(后台必备!) 7 大数问题 ...

  7. C#系列之算数运算符(四)

    今天,我将做一个算术运算符++和--的笔记以及一元运算符和二元运算符同时存在怎么计算的笔记 ++:分为前加加和后加加,但是最后结果都是+1: --:分为前减减和后减减,但是最后结果都是-1: 例如: ...

  8. 阿里云Redis性能测试结果(1个集合存300万数据,查询能几秒返回结果)

    现状: 1.买了一台主从的阿里云Redis,内存就1GB. 2.查询了阿里云的帮助,没有找到性能相关的说明, 有的也是4GB版本的并发性能 3.提工单问客服 一个集合里有300万数据,单次查询性能大概 ...

  9. 超实用的Eclipse快捷键大全(解密为什么他们的代码写的又快又好~)

    1.Ctrl+s:快速保存代码 一定要记得随时随地用Ctrl+s来保存我们的代码哦!!!不然等到电脑关机或者是使用的Eclipse突然闪退就欲哭无泪了.此时脑海里就突然出现了哔哔哔的画面~ 2.Alt ...

  10. python学习记录(八)

    0910--https://www.cnblogs.com/fnng/archive/2013/04/28/3048356.html Python异常 Python用异常对象(exception ob ...