Step1 Problem:
题目
给你一棵n个点树,m个操作,R是根,结果取模MOD.
操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z
操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和
操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z
操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和
Step2 Involving algorithms:
树链剖分
Step3 Ideas:
Step4 Code:
#include<bits/stdc++.h>
using namespace std;
#define lson root<<1
#define rson root<<1|1
#define MID int mid = (l+r)/2
const int N = 1e5+100;
vector<int> Map[N];
int w[N], wt[N], n, Root, MOD;
//w[i] i点的初始权值,wt[i] 映射后的点 初始权值
int fq[N], dep[N], siz[N], son[N], idx[N], top[N], cnt;
//fq[i] 点i的父亲,dep[i] 点i的深度,siz[i] 点i的儿子数(包括自己)
//son[i] 点i的重儿子, idx[i] 点i映射后的点,top[i] 该树链的根
int a[N<<2], lazy[N<<2];//维护区间和,和lazy标记
void Merge(int root)
{
lazy[root] = 0;
a[root] = (a[lson] + a[rson])%MOD;
}
void build(int root, int l, int r)
{
if(l == r)
{
a[root] = wt[l]%MOD;
return ;
}
MID;
build(lson, l, mid);
build(rson, mid+1, r);
Merge(root);
}
void pushdown(int root, int l, int r)
{
if(lazy[root])
{
MID;
a[lson] += (mid-l+1)*lazy[root];
a[lson] %= MOD;
a[rson] += (r-(mid+1)+1)*lazy[root];
a[rson] %= MOD;
lazy[lson] += lazy[root];
lazy[lson] %= MOD;
lazy[rson] += lazy[root];
lazy[rson] %= MOD;
lazy[root] = 0;
}
}
void updata(int root, int l, int r, int ul, int ur, int v)
{
if(ul <= l && r <= ur)
{
a[root] += (r-l+1)*v;
a[root] %= MOD;
lazy[root] += v;
lazy[root] %= MOD;
return ;
}
pushdown(root, l, r);
MID;
if(mid >= ul)
updata(lson, l, mid, ul, ur, v);
if(mid < ur) updata(rson, mid+1, r, ul, ur, v);
Merge(root);
}
int query(int root, int l, int r, int ul, int ur)
{
if(ul <= l && r <= ur)
{
return a[root];
}
pushdown(root, l, r);
MID;
int res = 0;
if(mid >= ul)
res += query(lson, l, mid, ul, ur), res %= MOD;
if(mid < ur) res += query(rson, mid+1, r, ul, ur), res %= MOD;
return res;
}
//以上都是线段树区间更新的代码,不会的话,请先学会。
void dfs1(int u, int f, int deep)//第一遍dfs,求fq[],dep[],siz[],son[]
{
fq[u] = f; dep[u] = deep;
siz[u] = 1; son[u] = -1;
int len = Map[u].size();
for(int i = 0; i < len; i++)
{
int to = Map[u][i];
if(to != f) {
dfs1(to, u, deep+1);
siz[u] += siz[to];
if(son[u] == -1 || siz[son[u]] < siz[to])
son[u] = to;
}
}
}
void dfs2(int u, int Top)//第二遍dfs,求idx[], top[]
{
idx[u] = cnt; top[u] = Top;
wt[cnt++] = w[u];
if(son[u] != -1)
dfs2(son[u], Top);
int len = Map[u].size();
for(int i = 0; i < len; i++)
{
int to = Map[u][i];
if(to != fq[u] && son[u] != to)
{
dfs2(to, to);
}
}
}
void add(int u, int v)
{
Map[u].push_back(v);
Map[v].push_back(u);
}
void updata_path(int x, int y, int z)//更新x->y的路径点权
{
int fx = top[x], fy = top[y];//树链根
while(fx != fy)//不在一条链上
{
if(dep[fx] < dep[fy])
swap(fx, fy), swap(x, y);
updata(1, 1, n, idx[fx], idx[x], z);//更新树链根 深度最大的链
x = fq[fx];//更新后 移动到树链根的父亲节点
fx = top[x], fy = top[y];
}
//在同一条链上,直接更新就好了
if(idx[x] > idx[y]) swap(x, y);
updata(1, 1, n, idx[x], idx[y], z);
}
int query_path(int x, int y)//求x->y路径点权和,跟上面思路一样
{
int fx = top[x], fy = top[y];
int res = 0;
while(fx != fy)
{
if(dep[fx] < dep[fy])
swap(fx, fy), swap(x, y);
res += query(1, 1, n, idx[fx], idx[x]), res %= MOD;
x = fq[fx];
fx = top[x], fy = top[y];
}
if(idx[x] > idx[y]) swap(x, y);
res += query(1, 1, n, idx[x], idx[y]), res %= MOD;
return res;
}
void updata_tree(int x, int z)//用到dfs序思想,子树的结点编号是连续的
{
updata(1, 1, n, idx[x], idx[x]+siz[x]-1, z);
}
int query_tree(int x)
{
return query(1, 1, n, idx[x], idx[x]+siz[x]-1);
}
void init()
{
Root = Root, cnt = 1;
}
int main()
{
int u, v, m;
cin >> n >> m >> Root >> MOD;
for(int i = 1; i <= n; i++)
scanf("%d", w+i);
for(int i = 1; i < n; i++)
{
scanf("%d %d", &u, &v);
add(u, v);
}
init();
dfs1(Root, -1, 1);
dfs2(Root, Root);
build(1, 1, n);
int ok, x, y, z;
while(m--)
{
scanf("%d", &ok);
if(ok == 1)
{
cin >> x >> y >> z;
updata_path(x, y, z);
}
else if(ok == 2)
{
cin >> x >> y;
printf("%d\n", query_path(x, y));
}
else if(ok == 3)
{
cin >> x >> z;
updata_tree(x, z);
}
else
{
cin >> x;
printf("%d\n", query_tree(x));
}
}
return 0;
}