Link:
LibreOJ https://loj.ac/problem/139
Preliminaries
树链剖分(重链剖分)的复杂度:
每次跳轻儿子,子树规模至少缩小一半
所以最多跳 (lfloor log n
floor) 次
树剖(重剖)求LCA:
所以就可以 (O(log n)) 用类似倍增的方法求 LCA
不过实现起来差别很大!
树剖的实现很喵喵!!
真·前置知识:DFS序
类似于括号序列,不同于前序遍历也不同于欧拉序。
比如说 结点A 左子树B 右子树C 左子树的左子树D
前序遍历:ABDC
DFS序:ABDDBCCA
欧拉序:ABDDBACCA
具体的基本操作,参考【笔记】树的DFS序
Solution
1 引入
首先明确一点,有四个操作不代表照搬四个操作就可以
比如
在 DFS序 中,修改子树仍然可以变成区间修改;修改路径仍然可以是单点修改
然后现在查询
比方说查询子树吧,对于修改子树的贡献直接区间查询就可以统计,对于修改路径的贡献也可以做
但是查询路径呢?
还是变成四次单点查询就不行了吧?因为贡献还可能来自路径修改
2 第一步
所以路径修改必须被拆出来,
如果只剩下只有左儿子的链就可以变成单独的多个区间修改?
emmmm
不对啊,什么左儿子,又不是二叉树
所谓的左儿子自己定义就可以,为了方便就直接定义成重儿子。
3 操作转化
那就很好处理了
dfs的时候先走重儿子可以使得同一重链结点编号连续
于是
树链修改就是多个区间修改
树链查询就是多个区间查询
(实际上实现的时候可以仿照LCA的方法做)
而子树很简单,仍然是dfs序的经典方法
子树修改 = 区间修改
子树查询 = 区间查询
并不是每条重链都开一个线段树!!
整体开一个就好。
4 换根
不过换根呢?
换根对路径操作没有一点影响
但是子树就不一样了:
可以发现 newroot → point → root 这条路径里面的点的子树在换根操作后,、
会变成 整棵树 减去 原root下(point往newroot那边的子结点)的子树
对这个区间进行操作就可以了
而不在这条路径里的不用这么做,写代码的时候别像我一样忘了。。
5 制杖时间
今天lazytag不会写调了一晚上
马上回去补.jpg
Code
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
const int MAXN = 1e5 + 10;
int n, m, cur_root = 1;
int tot = 0;
int head[MAXN], nxt[MAXN], to[MAXN], fa[MAXN];
#define add_edge(a, b) nxt[++tot] = head[a], head[a] = tot, to[tot] = b
int wson[MAXN], wson_weight[MAXN], siz[MAXN];
int in[MAXN], out[MAXN], timestamp = 0;
int depth[MAXN];
int infrom[MAXN];
int tval[MAXN];
int wlink_top[MAXN];
void dfs1(int x) {
for (int i = head[x]; i; i = nxt[i]) {
depth[to[i]] = depth[x] + 1;
dfs1(to[i]);
siz[x] += siz[to[i]];
if (siz[to[i]] > wson_weight[x]) {
wson_weight[x] = siz[to[i]];
wson[x] = to[i];
}
}
++siz[x];
}
void dfs2(int x, int wtp) {
wlink_top[x] = wtp;
in[x] = ++timestamp;
infrom[timestamp] = x;
if (wson[x])
dfs2(wson[x], (!wtp) ? wson[x] : wtp);
for (int i = head[x]; i; i = nxt[i]) {
if (to[i] == wson[x])
continue;
// dfs2(to[i], 0); ITS WRONG
dfs2(to[i], to[i]);
}
out[x] = timestamp;
}
const int SEGM = MAXN << 2;
int lc[SEGM], rc[SEGM];
int len[SEGM];
long long segval[SEGM];
void pushup(int pos) { segval[pos] = segval[pos << 1] + segval[pos << 1 | 1]; }
void seg_build(int pos, int l, int r) {
len[pos] = r - l + 1;
if (l == r) {
segval[pos] = tval[infrom[l]];
return;
}
int mid = l + r >> 1;
seg_build(pos << 1, l, mid);
seg_build(pos << 1 | 1, mid + 1, r);
pushup(pos);
}
int lca;
int get_lca(int u, int v) {
while (wlink_top[u] != wlink_top[v]) {
if (depth[wlink_top[u]] < depth[wlink_top[v]])
swap(u, v);
u = fa[wlink_top[u]];
}
if (depth[u] > depth[v])
swap(u, v);
return u;
}
long long addtag[SEGM];
long long pdtmp;
int llc, rrc;
void pushdown(int x) {
if (!addtag[x])
return;
pdtmp = addtag[x];
llc = x << 1, rrc = x << 1 | 1;
addtag[llc] += pdtmp;
addtag[rrc] += pdtmp;
segval[llc] += pdtmp * len[llc];
segval[rrc] += pdtmp * len[rrc];
addtag[x] = 0;
}
void seg_modify(int pos, int l, int r, int ml, int mr, int modval) {
if (ml > r || mr < l)
return;
if (ml <= l && mr >= r) {
if (l == r) {
segval[pos] += modval;
return;
}
addtag[pos] += modval;
pushdown(pos);
pushup(pos);
return;
}
pushdown(pos);
int mid = l + r >> 1;
seg_modify(pos << 1, l, mid, ml, mr, modval);
seg_modify(pos << 1 | 1, mid + 1, r, ml, mr, modval);
pushup(pos);
}
long long seg_query(int pos, int l, int r, int ql, int qr) {
if (ql > r || qr < l)
return 0;
if (ql <= l && qr >= r) {
if (l < r /*!!*/) {
pushdown(pos);
pushup(pos);
}
return segval[pos];
}
pushdown(pos);
int mid = l + r >> 1;
return seg_query(pos << 1, l, mid, ql, qr) + seg_query(pos << 1 | 1, mid + 1, r, ql, qr);
}
void pth_modify(int u, int v, int k) {
while (wlink_top[u] != wlink_top[v]) {
if (depth[wlink_top[u]] < depth[wlink_top[v]])
swap(u, v);
seg_modify(1, 1, n, in[wlink_top[u]], in[u], k);
u = fa[wlink_top[u]];
}
if (depth[u] > depth[v])
swap(u, v);
seg_modify(1, 1, n, in[u], in[v], k);
}
int get_cd(int pos) {
// if (u==pos) return -1; done by other functions
// find the nearest(of pos) cd s.t. current_root to cd to pos to 1
static int u, lst;
u = cur_root, lst = cur_root;
while (wlink_top[u][depth] > pos[depth]) {
lst = wlink_top[u];
u = wlink_top[u][fa];
}
if (u == pos)
return lst;
else
return infrom[in[u] - depth[u] + depth[pos] + 1]; //***
}
int cd;
void tre_modify(int u, int k) {
if (u == cur_root) {
seg_modify(1, 1, n, 1, n, k);
return;
} else if (in[u] <= in[cur_root] && out[cur_root] <= out[u]) {
cd = get_cd(u);
seg_modify(1, 1, n, in[cd], out[cd], -k);
seg_modify(1, 1, n, 1, n, k);
} else {
seg_modify(1, 1, n, in[u], out[u], k);
}
}
long long pth_query(int u, int v) {
long long ans = 0;
while (wlink_top[u] != wlink_top[v]) {
if (depth[wlink_top[u]] < depth[wlink_top[v]])
swap(u, v);
ans += seg_query(1, 1, n, in[wlink_top[u]], in[u]);
u = fa[wlink_top[u]];
}
if (depth[u] > depth[v])
swap(u, v);
ans += seg_query(1, 1, n, in[u], in[v]);
return ans;
}
long long tre_query(int u) {
if (u == cur_root)
return seg_query(1, 1, n, 1, n);
else if (in[u] <= in[cur_root] && out[cur_root] <= out[u]) {
cd = get_cd(u);
return seg_query(1, 1, n, 1, n) - seg_query(1, 1, n, in[cd], out[cd]);
} else
return seg_query(1, 1, n, in[u], out[u]);
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> tval[i];
}
for (int i = 2; i <= n; ++i) //!!
{
cin >> fa[i];
add_edge(fa[i], i);
}
depth[1] = 1;
dfs1(1);
dfs2(1, 1);
seg_build(1, 1, n);
cin >> m;
for (int opt, u, v, k, i = 1; i <= m; ++i) {
cin >> opt;
switch (opt) {
case 1:
cin >> u;
cur_root = u;
break;
case 2:
cin >> u >> v >> k;
pth_modify(u, v, k);
break;
case 3:
cin >> u >> k;
tre_modify(u, k);
break;
case 4:
cin >> u >> v;
cout << pth_query(u, v) << endl;
break;
case 5:
cin >> u;
cout << tre_query(u) << endl;
break;
}
}
return 0;
}