#include <bits/stdc++.h>
using namespace std;
typedef long long ll; int n, m, rt, mod, cnt, tot;
int val[100005];
int dep[100005];
int id[100005], ed[100005];
int rk[100005];
int sz[100005];
int fa[100005];
int son[100005];
int top[100005];
ll sum[400005];
ll lz[400005]; struct node {
int to, nex;
}E[200005];
int head[100005]; void dfs1(int x, int pre, int d) {
sz[x] = 1;
dep[x] = d;
fa[x] = pre;
for(int i = head[x]; i; i = E[i].nex) {
int v = E[i].to;
if(v == pre) continue; dfs1(v, x, d + 1);
sz[x] += sz[v];
if(sz[son[x]] < sz[v]) son[x] = v;
}
} void dfs2(int x, int t) {
top[x] = t;
id[x] = ++tot;
rk[tot] = x;
if(!son[x]) {
ed[x] = tot;
return;
} dfs2(son[x], t);
for(int i = head[x]; i; i = E[i].nex) {
int v = E[i].to;
if(v == son[x] || v == fa[x]) continue;
dfs2(v, v);
}
ed[x] = tot;
} void pushup(int rt) {
sum[rt] = (sum[rt << 1] + sum[rt << 1 | 1]) % mod;
} void pushdown(int l, int r, int rt) {
if(lz[rt]) {
int m = l + r >> 1;
sum[rt << 1] = (sum[rt << 1] + 1LL * (m - l + 1) * lz[rt] % mod) % mod;
sum[rt << 1 | 1] = (sum[rt << 1 | 1] + 1LL * (r - m) * lz[rt] % mod) % mod;
lz[rt << 1] = (lz[rt << 1] + lz[rt]) % mod;
lz[rt << 1 | 1] = (lz[rt << 1 | 1] + lz[rt]) % mod;
lz[rt] = 0;
}
} void build(int l, int r, int rt) {
if(l == r) {
sum[rt] = 1LL * val[rk[l]] % mod;
return;
} int m = l + r >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
pushup(rt);
} void update(int ql, int qr, ll val, int l, int r, int rt) {
if(ql <= l && qr >= r) {
sum[rt] += 1LL * (r - l + 1) * val % mod;
sum[rt] %= mod;
lz[rt] += val;
lz[rt] %= mod;
return;
} pushdown(l, r, rt);
int m = l + r >> 1;
if(ql <= m) update(ql, qr, val, l, m, rt << 1);
if(qr > m) update(ql, qr, val, m + 1, r, rt << 1 | 1);
pushup(rt);
} ll query(int ql, int qr, int l, int r, int rt) {
if(ql <= l && qr >= r) return sum[rt]; pushdown(l, r, rt);
ll res = 0; int m = l + r >> 1;
if(ql <= m) res += query(ql, qr, l, m, rt << 1);
if(qr > m) res += query(ql, qr, m + 1, r, rt << 1 | 1);
res %= mod;
return res;
} ll cal_sum(int x, int y) {
ll res = 0;
int fx = top[x];
int fy = top[y];
while(fx != fy) {
if(dep[fx] >= dep[fy]) {
res += query(id[fx], id[x], 1, n, 1);
res %= mod;
x = fa[fx]; fx = top[x];
} else {
res += query(id[fy], id[y], 1, n, 1);
res %= mod;
y = fa[fy]; fy = top[y];
}
}
if(id[x] <= id[y]) res += query(id[x], id[y], 1, n, 1);
else res += query(id[y], id[x], 1, n, 1);
res %= mod; return res;
} void update_lian(int x, int y, ll val) {
int fx = top[x];
int fy = top[y];
while(fx != fy) {
if(dep[fx] >= dep[fy]) {
update(id[fx], id[x], val, 1, n, 1);
x = fa[fx]; fx = top[x];
} else {
update(id[fy], id[y], val, 1, n, 1);
y = fa[fy]; fy = top[y];
}
}
if(id[x] <= id[y]) update(id[x], id[y], val, 1, n, 1);
else update(id[y], id[x], val, 1, n, 1);
} int main() {
scanf("%d%d%d%d", &n, &m, &rt, &mod);
cnt = 0;
tot = 0;
for(int i = 1; i <= n; i++) scanf("%d", &val[i]);
for(int i = 1; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
E[++cnt].to = y; E[cnt].nex = head[x]; head[x] = cnt;
E[++cnt].to = x; E[cnt].nex = head[y]; head[y] = cnt;
}
dfs1(rt, 0, 1);
dfs2(rt, rt);
build(1, n, 1);
while(m--) {
int opt;
scanf("%d", &opt); int a, b, c;
if(opt == 1) {
scanf("%d%d%d", &a, &b, &c);
c %= mod;
update_lian(a, b, 1LL * c);
} else if(opt == 2) {
scanf("%d%d", &a, &b);
printf("%lld\n", cal_sum(a, b));
} else if(opt == 3) {
scanf("%d%d", &a, &b);
b %= mod;
update(id[a], ed[a], 1LL * b, 1, n, 1);
} else {
scanf("%d", &a);
printf("%lld\n", query(id[a], ed[a], 1, n, 1));
}
}
return 0;
}

P3384 [模板] 树链剖分的更多相关文章

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

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

  2. [洛谷P3384] [模板] 树链剖分

    题目传送门 显然是一道模板题. 然而索引出现了错误,狂wa不止. 感谢神犇Dr_J指正.%%%orz. 建线段树的时候,第44行. 把sum[p]=bv[pos[l]]%mod;打成了sum[p]=b ...

  3. luoguP3384 [模板]树链剖分

    luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...

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

    树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常暴力优雅地解决很多问题. 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就 ...

  5. 模板 树链剖分BFS版本

    //点和线段树都从1开始 //边使用vector vector<int> G[maxn]; ],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn], ...

  6. 树链剖分详解(洛谷模板 P3384)

    洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 emm还有必备的 链式前向星.线段树 也要先学了. 如果这三个知识点没掌握好的话,树链剖 ...

  7. 『题解』洛谷P3384 【模板】树链剖分

    Problem Portal Portal1: Luogu Description 如题,已知一棵包含\(N\)个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作\(1\): ...

  8. [note]树链剖分

    树链剖分https://www.luogu.org/problemnew/show/P3384 概念 树链剖分,是一种将树剖分成多条不相交的链的算法,并通过其他的数据结构来维护这些链上的信息. 最简单 ...

  9. P3384 【模板】树链剖分

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

随机推荐

  1. Burp suite的系列介绍 (1)

    前言 为了进行Web安全方面的学习,Burp suite是必备的工具之一,我们将会从多个模块进行逐步的学习. Burp suite的应用场景 1.HTTP服务端接口测试. 2.HTTP客户端和HTTP ...

  2. 【CRS】vipca最后一步执行报错CRS-0215

    当我们在安装Clusterware 的时候, 需要在第二节点上vipca , 配置到最后安装的时候, 安装到 75% 左右,报错:     CRS-0215 : Could not start res ...

  3. kubernets之pod的标签拓展

    一 标签的拓展使用 1.1 标签的作用范围不仅仅适用于pod对node以及其他类的大部分资源同样适用 k label node node01 gpu=true k是kubectl的别名形式 同样对于n ...

  4. pg_rman的安装与使用

    1.下载对应数据库版本及操作系统的pg_rman源码 https://github.com/ossc-db/pg_rman/releases 本例使用的是centos6.9+pg10,因此下载的是pg ...

  5. kernel升级模式RKS让人振奋

    前几天刚将我的ERP内核从701_rel 升级到721_ext_rel,看到721的说明了讲到,对于这次的更新,支持RKS(Rolling Kernel Switch)了,简单的讲,就是以后对于内核的 ...

  6. 【Android】关于连续多次点击控件的控制方案(新建监听类)

    参考:防止Android过快点击造成多次事件的三种方法_胖胖的博客-CSDN博客 实现逻辑很简单: 设置限定时间 在用户点击时开始计时 若计时未超过限定时间,则不允许触发点击事件 因还未学习过Rxja ...

  7. web项目启动链接mysql巨慢

    说明:项目部署到测试服务器上,mysql部署在另一台服务器上,项目第一次启动之后登陆后台很慢,大概30s左右,经查发现第一次访问数据库的时候会通过DNS解析客户端机器域名,mysql还有DNS反向解析 ...

  8. RPC 实战与原理 精简版

    什么是 RPC? RPC 有什么作用? RPC 步骤 为什么需要序列化? 零拷贝 什么是零拷贝? 为什么需要零拷贝? 如何实现零拷贝? Netty 的零拷贝有何不同? 动态代理实现 HTTP/2 特性 ...

  9. 电脑微信电脑PC 多开/防撤回 补丁

    简介 经常使用微信电脑版的用户都会发现一个蛋疼的事情,那就是微信PC版不支持多开,也就是不能同时登陆多个账号,这对于需要在电脑上登陆多个微信账号的朋友来说肯定是极其的不方便.另外有的时候别人撤回了一些 ...

  10. Python+Selenium+Unittest实现PO模式web自动化框架(4)

    1.PageLocators目录下的具体模块 2.PageLocators目录下主要放置个页面的元素定位.用于统一管理个页面的定位元素. 例如:登录页面的元素定位login_page_locator. ...