用的是jiangly的板子,支持树上区间修改,区间查询,子树查询
using ll = long long;
using i64 = long long;
constexpr i64 inf = 1E18;
// Tag 的 apply 是将 tag 往下传, info 的 apply 是对自身的值进行修改
struct Tag {
i64 add = 0;
void apply(const Tag& t) {
add += t.add;
}
};
struct Info {
i64 min = inf;
i64 max = -inf;
i64 sum = 0;
i64 act = 0;
void apply(const Tag& t) {
min += t.add;
max += t.add;
sum += act * t.add;
}
};
Info operator+(const Info& a, const Info& b) {
Info c;
c.min = std::min(a.min, b.min);
c.max = std::max(a.max, b.max);
c.sum = a.sum + b.sum;
c.act = a.act + b.act;
return c;
}
template<class Info, class Tag>
struct LazySegmentTree {
int n;
std::vector<Info> info;
std::vector<Tag> tag;
LazySegmentTree() : n(0) {}
LazySegmentTree(int n_, Info v_ = Info()) {
init(n_, v_);
}
template<class T>
LazySegmentTree(const std::vector<T>& init_) {
init(init_);
}
void init(int n_, Info v_ = Info()) {
init(std::vector<Info>(n_, v_));
}
template<class T>
void init(const std::vector<T>& init_) {
n = init_.size();
info.assign(4 << (int)std::log2(n), Info());
tag.assign(4 << (int)std::log2(n), Tag());
std::function<void(int, int, int)> build = [&](int p, int l, int r) {
if (r - l == 1) {
info[p] = init_[l];
return;
}
int m = (l + r) / 2;
build(2 * p, l, m);
build(2 * p + 1, m, r);
pull(p);
};
build(1, 0, n);
}
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
void apply(int p, const Tag& v) {
info[p].apply(v);
tag[p].apply(v);
}
void push(int p) {
apply(2 * p, tag[p]);
apply(2 * p + 1, tag[p]);
tag[p] = Tag();
}
void modify(int p, int l, int r, int x, const Info& v) {
if (r - l == 1) {
info[p] = v;
return;
}
int m = (l + r) / 2;
push(p);
if (x < m) {
modify(2 * p, l, m, x, v);
}
else {
modify(2 * p + 1, m, r, x, v);
}
pull(p);
}
void modify(int p, const Info& v) {
modify(1, 0, n, p, v);
}
Info rangeQuery(int p, int l, int r, int x, int y) {
if (l >= y || r <= x) {
return Info();
}
if (l >= x && r <= y) {
return info[p];
}
int m = (l + r) / 2;
push(p);
return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
}
Info rangeQuery(int l, int r) {
return rangeQuery(1, 0, n, l, r);
}
void rangeApply(int p, int l, int r, int x, int y, const Tag& v) {
if (l >= y || r <= x) {
return;
}
if (l >= x && r <= y) {
apply(p, v);
return;
}
int m = (l + r) / 2;
push(p);
rangeApply(2 * p, l, m, x, y, v);
rangeApply(2 * p + 1, m, r, x, y, v);
pull(p);
}
void rangeApply(int l, int r, const Tag& v) {
return rangeApply(1, 0, n, l, r, v);
}
};
LazySegmentTree<Info, Tag> l;
struct HLD {
int n;
std::vector<int> siz, top, dep, parent, in, out, seq;
std::vector<std::vector<int>> adj;
int cur;
HLD() {}
HLD(int n) {
init(n);
}
void init(int n) {
this->n = n;
siz.resize(n); // 以u为根的子树节点个数
top.resize(n); // 重链所在顶点
dep.resize(n); // u的深度
parent.resize(n); // u的父节点
in.resize(n);
out.resize(n);
seq.resize(n);
cur = 0;
adj.assign(n, {}); // son[i]==adj[i][0]
}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void work(int root = 0) {
top[root] = root;
dep[root] = 0;
parent[root] = -1;
dfs1(root);
dfs2(root);
}
void dfs1(int u) {
if (parent[u] != -1) {
adj[u].erase(std::find(adj[u].begin(), adj[u].end(), parent[u]));
}
siz[u] = 1;
for (auto& v : adj[u]) {
parent[v] = u;
dep[v] = dep[u] + 1;
dfs1(v);
siz[u] += siz[v];
if (siz[v] > siz[adj[u][0]]) {
std::swap(v, adj[u][0]);
}
}
}
void dfs2(int u) {
in[u] = cur++;
seq[in[u]] = u;
for (auto v : adj[u]) {
top[v] = (v == adj[u][0] ? top[u] : v);
dfs2(v);
}
out[u] = cur;
}
int lca(int u, int v) {
while (top[u] != top[v]) {
if (dep[top[u]] > dep[top[v]]) {
u = parent[top[u]];
}
else {
v = parent[top[v]];
}
}
return (dep[u] < dep[v]) ? u : v;
}
i64 query_path(int u, int v) {
i64 ans = 0;
while (top[u] != top[v]) {
if (dep[top[u]] > dep[top[v]]) {
ans += l.rangeQuery(in[top[u]], in[u] + 1).sum;
u = parent[top[u]];
}
else {
ans += l.rangeQuery(in[top[v]], in[v] + 1).sum;
v = parent[top[v]];
}
}
if (dep[u] < dep[v])std::swap(u, v);
ans += l.rangeQuery(in[v], in[u] + 1).sum;
return ans;
}
void update_path(int u, int v, int k) {
Tag t;
t.add = k;
while (top[u] != top[v]) {
if (dep[top[u]] > dep[top[v]]) {
l.rangeApply(in[top[u]], in[u] + 1, t);
u = parent[top[u]];
}
else {
l.rangeApply(in[top[v]], in[v] + 1, t);
v = parent[top[v]];
}
}
if (dep[u] < dep[v])std::swap(u, v);
l.rangeApply(in[v], in[u] + 1, t);
}
void update_tree(int u, int k) {
Tag t;
t.add = k;
l.rangeApply(in[u], in[u] + siz[u], t);
}
i64 query_tree(int u) {
return l.rangeQuery(in[u], in[u] + siz[u]).sum;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n, m, r, p;
// 节点个数, 操作数, 根节点, 模数
std::cin >> n >> m >> r >> p;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
HLD hld(n + 1);
for (int i = 1; i < n; i++) {
int x, y;
std::cin >> x >> y;
hld.addEdge(x, y);
}
hld.work(r);
std::vector<Info> init(n + 1);
for (int i = 1; i <= n; i++) {
init[hld.in[i]].sum = a[i];
init[hld.in[i]].act = 1;
}
l.init(init);
while (m--) {
int op, x, y, z;
std::cin >> op >> x;
if (op == 1) {
std::cin >> y >> z;
hld.update_path(x, y, z);
}
else if (op == 2) {
std::cin >> y;
std::cout << hld.query_path(x, y) % p << '\n';
}
else if (op == 3) {
std::cin >> z;
hld.update_tree(x, z);
}
else {
std::cout << hld.query_tree(x) % p << '\n';
}
}
return 0;
}