参考博客: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. DNS隧道基础

    DNS协议是一种请求/应答协议,也是一种可用于应用层的隧道技术.虽然激增的DNS流量可能会被发现,但基于传统socket隧道已经濒临淘汰鸡TCP.UDP通信大量被防御系统拦截的状况,DNS.ICMP. ...

  2. 超链接a标签的伪类选择器问题,Link标签与visited标签的失效问题(问题介绍与解决方法)。

    <!DOCTYPE html>< html>< head>     <meta charset="utf-8" />     < ...

  3. BaseAdapter的三种表达式分析,startActivityForResult的使用

    (一)BaseAdapter的三种表达式: ①逗比式: public View getView(int position, View convertView, ViewGroup parent) { ...

  4. 实验8:路由器IOS升级2

    IOS 升级 在介绍CISCO路由器IOS升级方法前,有必要对Cisco路由器的存储器的相关知识作以简单介绍.路由器与计算机相似,它也有内存和操作系统.在Cisco路由器中,其操作系统叫做互连网操作系 ...

  5. RestTemplate远程调用方法

    概述: spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值 ...

  6. Mysql设置创建时间字段和更新时间字段自动获取时间,填充时间

    1.引言在实际开发中,每条数据的创建时间和修改时间,尽量不需要应用程序去记录,而由数据库获取当前时间自动记录创建时间,获取当前时间自动记录修改时间. 2.创建语句(1)–添加CreateTime 设置 ...

  7. 深入理解JVM(学习过程)

    这,仅是我学习过程中记录的笔记.确定了一个待研究的主题,对这个主题进行全方面的剖析.笔记是用来方便我回顾与学习的,欢迎大家与我进行交流沟通,共同成长.不止是技术. 2020年02月06日22:43:0 ...

  8. 手把手带你阅读Mybatis源码(一)构造篇

    前言 今天会给大家分享我们常用的持久层框架——MyBatis的工作原理和源码解析,后续会围绕Mybatis框架做一些比较深入的讲解,之后这部分内容会归置到公众号菜单栏:连载中…-框架分析中,欢迎探讨! ...

  9. AtCoder Beginner Contest 156

    https://atcoder.jp/contests/abc156/tasks A - Beginner #include <bits/stdc++.h> #define ll long ...

  10. ATTENTION NETWORK分析

    1. TensorFlowTrainable类 1 class TensorFlowTrainable(object): 2 def __init__(self): 3 self.parameters ...