//P3384 【模板】轻重链剖分
//2020.11.10
//将树从x到y结点最短路径上所有节点的值都加上z
//求树从x到y结点最短路径上所有节点的值之和
//将以x为根节点的子树内所有节点值都加上z
//求以x为根节点的子树内所有节点值之和
#include <bits/stdc++.h>
#define ll long long
#define _for(i,a,b) for(ll i = (a);i < (b); ++i)
#define sc scanf
#define pr printf
#define TLE ios::sync_with_stdio(false); cin.tie(0);
const int maxn=200000 + 5;
const int INF = 0x3f3f3f3f;
using namespace std;
int depth[maxn];
int fa[maxn]; //father node
int sbs[maxn]; //subtree's node size
int son[maxn]; //每个非叶子节点的重儿子编号
int n,m,r,p; //n: 树的节点个数
//m: 操作个数
//r: 根节点序号
//p: 取模数
int w[maxn]; //各节点权值
int id[maxn]; //标记每个点的新编号
int cnt = 0; //记录总编号数
int _w[maxn]; //新编号节点的权值
int top[maxn]; //节点所在链的最顶端节点
struct Edge{
int t,nex;
}e[maxn];
int head[maxn] = {0},tot = 0;
inline void add(int u, int v) {
e[++tot].t = v;
e[tot].nex = head[u];
head[u] = tot;
}
//线段树:
int tree[maxn<<2] = {0}, tag[maxn<<2] = {0};
void push_down(int node,int start,int end,int val) {
tag[node] = (tag[node] + val) % p;
tree[node] = (tree[node] + val * (end - start + 1)) % p;
}
void build_tree(int node,int start,int end){
if(start == end) {
tree[node] = _w[start + 1];
}
else {
int mid = start + end >> 1;
int left_node = (node << 1 ) + 1;
int right_node = (node << 1 ) + 2;
build_tree(left_node, start, mid);
build_tree(right_node, mid + 1, end);
tree[node] = (tree[left_node] + tree[right_node]);
}
}
void updata_tree(int node,int start,int end,int L,int R,int val){
//printf("start : %lld end : %lld\n",start,end); getchar();
if(R < start || L > end) {
return;
}
else if(start >= L && end <= R) {
tree[node] += val * (end - start + 1) % p;
tag[node] += val % p;
return;
}
else {
int mid = start + end >> 1;
int left_node = (node << 1 ) + 1;
int right_node = (node << 1 ) + 2;
if(tag[node] != 0) {
push_down(left_node, start, mid, tag[node]);
push_down(right_node, mid+1, end, tag[node]);
tag[node] = 0;
}
updata_tree(left_node, start, mid, L, R, val);
updata_tree(right_node, mid+1, end, L, R, val);
tree[node] = (tree[left_node] + tree[right_node]) % p;
}
}
int query_tree(int node,int start,int end,int L,int R){
//printf("start : %lld end : %lld\n",start,end); getchar();
if(R < start || L > end) {
return 0;
}
else if(start >= L && end <= R) {
return tree[node];
}
else {
int mid = start + end >> 1;
int left_node = (node << 1 ) + 1;
int right_node = (node << 1 ) + 2;
if(tag[node] != 0) {
push_down(left_node, start, mid, tag[node]);
push_down(right_node, mid+1, end, tag[node]);
tag[node] = 0;
}
int left_sum = query_tree(left_node, start, mid, L, R) % p;
int right_sum = query_tree(right_node, mid+1, end, L, R) % p;
//printf("l : %lld r : %lld\n",left_sum,right_sum);
return left_sum + right_sum;
}
}
//标记每个节点的深度depth[]
//标记每个节点的父亲节点fa[]
//标记每个非叶子节点的子树大小(含自身节点)
//标记每个非叶子节点的重儿子编号son[]
inline void dfs1(int now, int fath) {
depth[now] = depth[fath] + 1;
fa[now] = fath;
sbs[now] = 1;
int mss = -1; //maxson size
for(int i = head[now]; i; i = e[i].nex) if(e[i].t != fath) {
int v = e[i].t;
dfs1(v,now);
sbs[now] += sbs[v];
if(sbs[now] > mss) {
mss = sbs[v];
son[now] = v;
}
}
}
//标记每个点的新编号
//赋值每个点的初始值到新编号上
//处理每个点所在链的顶端
//处理每条链
inline void dfs2(int now, int topf) { //topf: 当前链的最顶端节点
id[now] = ++cnt;
_w[cnt] = w[now];
top[now] = topf;
if(!son[now]) return; //此节点没有重儿子,为叶子节点
dfs2(son[now],topf);
for(int i = head[now]; i; i = e[i].nex) if(e[i].t != fa[now] && e[i].t != son[now]) {
dfs2(e[i].t, e[i].t);
}
}
int res = 0;
//求树从x到y结点最短路径上所有节点的值之和
inline int qRange(int x,int y) {
int ans = 0;
while(top[x] != top[y]) {
if(depth[top[x]] < depth[top[y]]) { //不妨设top[x]的深度 >= top[y]的深度
swap(x,y);
}
ans += query_tree(0,0,n-1,id[top[x]]-1,id[x]-1); //x点到x所在链顶端这一区间段的点权和
ans %= p;
x = fa[top[x]];
}
if(depth[x] > depth[y]) swap(x,y);
ans += query_tree(0,0,n-1,id[x]-1,id[y]-1) % p;
ans %= p;
return ans;
}
//求以x为根节点的子树内所有节点值之和
inline int qSon(int x) {
return query_tree(0,0,n-1,id[x]-1,id[x]+sbs[x]-2) % p;
}
//将树从x到y结点最短路径上所有节点的值都加上z
inline void updRange(int x,int y,int k) {
k %= p;
while(top[x] != top[y]) {
if(depth[top[x]] < depth[top[y]]) swap(x,y);
updata_tree(0,0,n-1,id[top[x]]-1,id[x]-1,k);
x = fa[top[x]];
}
if(depth[x] > depth[y]) swap(x,y);
updata_tree(0,0,n-1,id[x]-1,id[y]-1,k);
}
//将以x为根节点的子树内所有节点值都加上z
inline void updSon(int x,int k) {
k %= p;
updata_tree(0,0,n-1,id[x]-1,id[x]+sbs[x]-2,k);
}
int main() {
sc("%d %d %d %d",&n,&m,&r,&p);
_for(i,1,n+1) {
sc("%d",&w[i]);
}
int u,v;
_for(i,1,n) {
sc("%d %d",&u,&v);
add(u,v);
add(v,u);
}
dfs1(r,0);
dfs2(r,r);
build_tree(0,0,n-1);
while(m--) {
int k,x,y,z;
sc("%d",&k);
if(k == 1) {
sc("%d %d %d", &x, &y, &z);
updRange(x,y,z);
}
else if(k == 2) {
sc("%d %d", &x, &y);
pr("%d\n", qRange(x,y));
}
else if(k == 3) {
sc("%d %d", &x, &y);
updSon(x,y);
}
else if(k == 4) {
sc("%d",&x);
pr("%d\n",qSon(x));
}
}
return 0;
}
树链剖分-模板
最新推荐文章于 2024-04-15 16:44:08 发布