P3384 轻重链剖分
复杂数据结构++
树链剖分,极大地提高了树上元素的查询修改的效率。
这里是轻重链剖分,通过将对树的dfs,在求出每个节点的父亲,深度,子树大小,重儿子编号的同时,把树分割成了若干条重链,每条链都有独一无二的top节点
然后将这些链逐一标号映射到序列中,在轻重链剖分下每一个节点及其子树的下表在序列中连续。
因此就可以通过线段树之类的数据结构在log级别的时间内修改查询
#pragma GCC optimize(2)
#include
#define ll long long
#define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
double pi = acos(-1);
const double eps = 1e-12;
const int maxn = 1e5 + 10;
const int inf = 1e9;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
return f * x;
}
ll a[maxn], wt[maxn], mod;
int n, m, S;
int head[maxn], deep[maxn], fa[maxn], siz[maxn], son[maxn], id[maxn], top[maxn], edge_cnt = 0, cnt = 0;
struct edge {
int to, next;
}e[2 * maxn];
inline void add(int from, int to)
{
e[++edge_cnt].to = to;
e[edge_cnt].next = head[from];
head[from] = edge_cnt;
}
void dfs1(int now, int f, int dee)//先记录每个点的深度,子树大小,父节点编号,重儿子编号
{
deep[now] = dee;
fa[now] = f;
siz[now] = 1;
int maxson = -1;
for (int i = head[now]; i; i = e[i].next)
{
int to = e[i].to;
if (to == f)continue;
dfs1(to, now, dee + 1);
siz[now] += siz[to];
if (siz[to] > maxson)son[now] = to, maxson = siz[to];
}
}
void dfs2(int now, int topf)
{
id[now] = ++cnt;
wt[cnt] = a[now];//将节点值映射到序列
top[now] = topf;
if (!son[now])return;
dfs2(son[now], topf);//先处理重儿子
for (int i = head[now]; i; i = e[i].next)
{
int to = e[i].to;
if (to == fa[now] || to == son[now])continue;
dfs2(to, to);//遍历轻儿子,然而轻儿子还是会先遍历重边,所以每一条重链新编号是连续的
}
//最终子树的编号也是连续的,因此可以用线段树查询子树和
}
struct tree {
int ls;
int rs;
long long vue = 0;
long long lazy = 0;
}t[4 * maxn];
void build(int l, int r, int p)
{
t[p].ls = l;
t[p].rs = r;
if (l == r) { t[p].vue = wt[l] % mod; return; }
int mid = l + r >> 1;
build(l, mid, p << 1);
build(mid + 1, r, (p << 1) + 1);
t[p].vue = (t[p << 1].vue + t[(p << 1) + 1].vue) % mod;
}
inline void spread(int x)//标记下传
{
if (t[x].lazy)//如果该节点标记不为0
{
t[x * 2].vue += t[x].lazy * (ll)(t[x * 2].rs - t[x * 2].ls + 1);//更新左子区间和
t[x * 2 + 1].vue += t[x].lazy * (ll)(t[x * 2 + 1].rs - t[x * 2 + 1].ls + 1);//更新右子区间和
t[x * 2].lazy += t[x].lazy;
t[x * 2 + 1].lazy += t[x].lazy;//向子节点传递标记
t[x].lazy = 0;//初始化标记
}
}
void update_add(int x, int y, ll v, int p)
{
ll l = t[p].ls;
ll r = t[p].rs;
if (l >= x && r <= y)
{
t[p].vue += (long long)v * (r - l + 1);
t[p].lazy += v;
return;
}
spread(p);
int mid = l + r >> 1;
if (x <= mid)update_add(x, y, v, p << 1);
if (y > mid)update_add(x, y, v, (p << 1) + 1);
t[p].vue = t[p << 1].vue + t[(p << 1) + 1].vue;
}
long long query(int x, int y, int p)
{
int l = t[p].ls;
int r = t[p].rs;
if (l >= x && r <= y)
{
return t[p].vue % mod;
}
int mid = l + r >> 1;
long long ans = 0;
spread(p);
if (x <= mid)ans = (ans + query(x, y, p << 1)) % mod;
if (y > mid)ans = (ans + query(x, y, (p << 1) + 1)) % mod;
return ans;
}
void update_range(int x, int y,ll v)
{
v %= mod;
while (top[x] != top[y])
{
if (deep[top[y]] > deep[top[x]])swap(x, y);
update_add(id[top[x]], id[x], v, 1);
x = fa[top[x]];
}
if (deep[x] > deep[y])swap(x, y);
update_add(id[x], id[y], v, 1);
}
ll query_range(int x,int y)
{
ll res = 0;
while (top[x] != top[y])
{
if (deep[top[y]] > deep[top[x]])swap(x, y);
res = (res + query(id[top[x]], id[x], 1)) % mod;
x = fa[top[x]];
}
if (deep[x] > deep[y])swap(x, y);
res = (res + query(id[x], id[y], 1)) % mod;
return res;
}
int main()
{
fastio;
cin >> n >> m >> S >> mod;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i < n; i++)
{
int x, y;
cin >> x >> y;
add(x, y);
add(y, x);
}
dfs1(S, 0, 1);
dfs2(S, S);
build(1, n, 1);
ll o, x, y, z;
while (m--)
{
cin >> o;
if (o == 1)
{
cin >> x >> y >> z;
update_range(x, y, z);
}
else if (o == 2)
{
cin >> x >> y;
cout << query_range(x, y) << endl;
}
else if (o == 3)
{
cin >> x >> z;
update_add(id[x], id[x] + siz[x] - 1, z, 1);
}
else
{
cin >> x;
cout << query(id[x], id[x] + siz[x] - 1, 1) << endl;
}
}
return 0;
}