参考博客: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. spring mvc 框架运行机制 + 数据绑定原理

    spring mvc 运行主要的组件: 1 前端控制器 (dispatchservlet) 相当于一个重要处理器,它用来调用其他功能模块来分工的效应一次请求,主要起调度的作用. 2. handler ...

  2. Oracle数据库安装与卸载

    一.下载俩个压缩包,同时选中解压到一个文件夹中 二.点击step.exe(win10可能弹出不满足环境要求,选择是就行了) 三.把接收更新勾掉不需要 四.选择创建和配置数据库 五.选择服务器类 六.选 ...

  3. 【学习笔记】Linux基础(二):Linux的基本操作

    二.Linux的基本操作 0.正确的开关机操作 开机和登陆: 安全起见,一般不使用最高权限的root账户登入系统,光立系统时再使用 登录时为login程序提供账户名和密码即可,密码不会被显示,登陆后显 ...

  4. Java 设计模式之抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽 ...

  5. LeetCode 200. Number of Islands 岛屿数量(C++/Java)

    题目: Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is s ...

  6. 每日一练_PAT_B1001

    鲁宾逊先生有一只宠物猴,名叫多多.这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生!——熊字”.鲁宾逊先生和多多都很开心,因为花生正是他们的最爱.在 ...

  7. Codeforces_837

    A.扫一遍. #include<bits/stdc++.h> using namespace std; int n; string s; int main() { cin >> ...

  8. HDU_5094_dfs

    http://acm.hdu.edu.cn/showproblem.php?pid=5094 bfs,vis[x][y][z],z表示钥匙的状态,用二进制来表示,key[x][y]储存当前位置钥匙的二 ...

  9. golang函数 和 条件语句

    /* if : if 语句 由一个布尔表达式后紧跟一个或多个语句组成 is else : if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行 s ...

  10. Ubuntu下LAMP的环境配置教程

    总体来说,Ubuntu下安装LAMP环境是比较简单的,只需按照命令行执行即可,记录操作以备不时之需. 一,首先更新Ubuntu里面所有的软件 sudo apt-get update 二.之后安装Apa ...