树链剖分(树上问题)
树链剖分(树上点权问题)
1、树链剖分之树上线段树:求解路径值,路径大小,路径 \(rmq\),等等 \(...\) (求路径点权之和)。
2、时间复杂度:\(O(nlog^{2}n)\),空间复杂度:\(O(nlogn)\)。
template<typename T>
struct X_tree{
struct Segtree{
int siz, l, r;
T lzy, sum;
Segtree(int siz = 0, int l = 0, int r = 0, T lzy = 0, T sum = 0) : siz(siz), l(l), r(r), lzy(lzy), sum(sum) {}
#define ls (root << 1)
#define rs (root << 1 | 1)
#define ls_ tree[ls]
#define rs_ tree[rs]
#define rt_ tree[root]
};
int n, m;
vector<T> a;
vector<Segtree> tree;
X_tree() {}
X_tree(int n_) : a(n_ + 1), tree(n_ << 2) {
n = n_;
build(1, 1, n);
}
inline void init() {
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
}
inline Segtree hb(Segtree i, Segtree j) {
Segtree k;
k.siz = i.siz + j.siz;
k.l = i.l;
k.r = j.r;
k.sum = i.sum + j.sum;
k.sum %= mod;
return k;
}
inline void push_up(int root) {
rt_ = hb(ls_, rs_);
}
inline void build(int root, int l, int r) {
if (l == r) {
tree[root] = Segtree(1, l, r, 0, a[l] % mod);
return;
}
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
push_up(root);
}
inline void push_mark(int root) {
int l = rt_.l, r = rt_.r;
if (rt_.lzy) {
ls_.lzy += rt_.lzy;
rs_.lzy += rt_.lzy;
ls_.sum += rt_.lzy * ls_.siz;
rs_.sum += rt_.lzy * rs_.siz;
ls_.lzy %= mod;
ls_.sum %= mod;
rs_.lzy %= mod;
rs_.sum %= mod;
rt_.lzy = 0;
}
}
inline Segtree query(int root, int ql, int qr) {
int l = rt_.l, r = rt_.r;
if (r < ql || l > qr) {
return Segtree();
}
if (l >= ql && r <= qr) {
return rt_;
}
push_mark(root);
int mid = l + r >> 1;
return hb(query(ls, ql, qr), query(rs, ql, qr));
}
inline void update(int root, int ql, int qr, const T &opt = 1) {
int l = rt_.l, r = rt_.r;
if (r < ql || l > qr) {
return;
}
if (l >= ql && r <= qr) {
rt_.lzy += opt;
rt_.sum += opt * rt_.siz % mod;
rt_.lzy %= mod;
rt_.sum %= mod;
return;
}
push_mark(root);
int mid = l + r >> 1;
update(ls, ql, qr, opt);
update(rs, ql, qr, opt);
push_up(root);
}
};
template<typename T>
struct Sp{
struct node{
int to, nxt;
T w;
node(int to = 0, int nxt = 0, T w = 0) : to(to), nxt(nxt), w(w) {}
};
int n, m, cnt, root, tot;
vector<T> W, idw;// 点权、编号对应的点权
vector<int> id;// 每个节点在树链剖分dfs2遍历序列的最早编号
vector<int> dep, fa, son, siz, top, head, dfn;
vector<node> edg;
X_tree<T> tr;
Sp() {}
Sp(int n_, int m_, int root_ = 1)
: dep(n_ + 1), fa(n_ + 1), son(n_ + 1),
siz(n_ + 1), top(n_ + 1), head(n_ + 1),
edg(m_ << 1 | 1), dfn(n_ + 1), W(n_ + 1),
id(n_ + 1), idw(n_ + 1) {
tot = 0;
cnt = 0;
n = n_;
m = m_;
root = root_;
tr = X_tree<T>(n_);
}
inline void add(int u, int v, const T &w) {
++cnt;
edg[cnt].to = v;
edg[cnt].nxt = head[u];
edg[cnt].w = w;
head[u] = cnt;
}
inline void dfs1(int u, int pa) {
fa[u] = pa;
siz[u] = 1;
dep[u] = dep[pa] + 1;
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa) {
continue;
}
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > siz[son[u]]) {
son[u] = v;
}
}
}
inline void dfs2(int u, int topx) {
top[u] = topx;
id[u] = ++tot;
idw[tot] = W[u];
if (son[u]) {
dfs2(son[u], topx);
} else {
return;
}
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == fa[u] || v == son[u]) {
continue;
}
dfs2(v, v);
}
}
inline void init(int n_, int m_, int root_ = 1) {
tot = 0;
cnt = 0;
n = n_;
m = m_;
root = root_;
tr = X_tree<T>(n_);
dep.assign(n_ + 1, 0);
fa.assign(n_ + 1, 0);
son.assign(n_ + 1, 0);
siz.assign(n_ + 1, 0);
top.assign(n_ + 1, 0);
head.assign(n_ + 1, 0);
edg.assign(m_ << 1 | 1, node());
dfn.assign(n_ + 1, 0);
W.assign(n_ + 1, 0);
id.assign(n_ + 1, 0);
idw.assign(n_ + 1, 0);
}
inline void deal() {// 预处理
dfs1(root, 0);
dfs2(root, root);
for (int i = 1; i <= n; i++) {
upd_point(i, W[i]);
}
}
inline int lca(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) {
swap(x, y);
}
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
inline void upd_Range(int x, int y, T k) {// 最短路径上的点权修改
k %= mod;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) {
swap(x, y);
}
int l = id[top[x]], r = id[x];
tr.update(1, l, r, k);
x = fa[top[x]];
}
if (dep[x] > dep[y]) {
swap(x, y);
}
int l = id[x], r = id[y];
tr.update(1, l, r, k);
}
inline void upd_point(int x, T k) {// 单点的点权修改
tr.update(1, id[x], id[x], k);
}
inline void upd_tree(int x, T k) {// 以x为根的子树的点权修改
int l = id[x], r = l + siz[x] - 1;
tr.update(1, l, r, k);
}
inline T qur_Range(int x, int y) {// 最短路径的点权之和
T ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) {
swap(x, y);
}
int l = id[top[x]], r = id[x];
ans = ans + tr.query(1, l, r).sum;
x = fa[top[x]];
}
if (dep[x] > dep[y]) {
swap(x, y);
}
int l = id[x], r = id[y];
ans = ans + tr.query(1, l, r).sum;
ans %= mod;
return ans;
}
inline T qur_point(int x) {// 单点查询
return tr.query(1, id[x], id[x]).sum;
}
inline T qur_tree(int x) {// 一颗树的点权之和
int l = id[x], r = l + siz[x] - 1;
return tr.query(1, l, r).sum;
}
};
void solve() {
int n, m, root;
std::cin >> n >> m >> root >> mod;
Sp<i64> tr(n, n - 1, root);
for (int i = 1; i <= n; i++) {
std::cin >> tr.W[i];
}
for (int i = 1, u, v; i < n; i++) {
i64 w = 1;
std::cin >> u >> v;
tr.add(u, v, w);
tr.add(v, u, w);
}
tr.deal();
for (int i = 1; i <= m; i++) {
int opt;
std::cin >> opt;
if (opt == 1) {
int x, y;
i64 k;
std::cin >> x >> y >> k;
tr.upd_Range(x, y, k);
} else if (opt == 2) {
int x, y;
std::cin >> x >> y;
std::cout << tr.qur_Range(x, y) << '\n';
} else if (opt == 3) {
int x;
i64 k;
std::cin >> x >> k;
tr.upd_tree(x, k);
} else {
int x;
std::cin >> x;
std::cout << tr.qur_tree(x) << '\n';
}
}
}
树链剖分(树上边权问题)
1、求边权和求点权是差不多的,无非就是将边的权值赋予给和他相邻的点即可。
2、模版:洛谷P4114,洛谷P4116,提升:洛谷树剖边权问题提升题库。
template<typename T>
struct X_tree{
struct Segtree{
int siz, l, r;
T mx, sum;
Segtree(int siz = 0, int l = 0, int r = 0, T mx = 0, T sum = 0) : siz(siz), l(l), r(r), mx(mx), sum(sum) {}
#define ls (root << 1)
#define rs (root << 1 | 1)
#define ls_ tree[ls]
#define rs_ tree[rs]
#define rt_ tree[root]
};
int n, m;
vector<T> a;
vector<Segtree> tree;
X_tree() {}
X_tree(int n_) : a(n_ + 1), tree(n_ << 2) {
n = n_;
build(1, 1, n);
}
inline void init() {
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
}
inline Segtree hb(Segtree i, Segtree j) {
Segtree k;
k.siz = i.siz + j.siz;
k.l = i.l;
k.r = j.r;
k.mx = max(i.mx, j.mx);
k.sum = i.sum + j.sum;
return k;
}
inline void push_up(int root) {
rt_ = hb(ls_, rs_);
}
inline void build(int root, int l, int r) {
if (l == r) {
tree[root] = Segtree(1, l, r, a[l], a[l]);
return;
}
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
push_up(root);
}
inline void push_mark(int root) {
int l = rt_.l, r = rt_.r;
}
inline Segtree query(int root, int ql, int qr) {
int l = rt_.l, r = rt_.r;
if (r < ql || l > qr) {
return Segtree();
}
if (l >= ql && r <= qr) {
return rt_;
}
push_mark(root);
int mid = l + r >> 1;
return hb(query(ls, ql, qr), query(rs, ql, qr));
}
inline void update(int root, int ql, int qr, const T &opt = 1) {
int l = rt_.l, r = rt_.r;
if (r < ql || l > qr) {
return;
}
if (l >= ql && r <= qr) {
rt_.mx = opt;
rt_.sum = opt;
return;
}
push_mark(root);
int mid = l + r >> 1;
update(ls, ql, qr, opt);
update(rs, ql, qr, opt);
push_up(root);
}
};
template<typename T>
struct Sp{
struct node{
int to, nxt, ids;// ids表示这条边的编号,用于存之后边所连接的下节点
T w;
node(int to = 0, int nxt = 0, int ids = 0, T w = 0) : to(to), nxt(nxt), ids(ids), w(w) {}
};
int n, m, cnt, root, tot;
vector<T> W, idw;
vector<int> id;
vector<int> dep, fa, son, siz, top, head, dfn;
vector<int> edge;
vector<node> edg;
X_tree<T> tr;
Sp(int n_, int m_, int root_ = 1)
: dep(n_ + 1), fa(n_ + 1), son(n_ + 1),
siz(n_ + 1), top(n_ + 1), head(n_ + 1),
edg(m_ << 1 | 1), dfn(n_ + 1), W(n_ + 1),
id(n_ + 1), idw(n_ + 1), edge(m_ + 1) {
tot = 0;
cnt = 0;
n = n_;
m = m_;
root = root_;
tr = X_tree<T>(n_);
}
inline void add(int u, int v, const T &w, const int ids = 0) {
++cnt;
edg[cnt].to = v;
edg[cnt].nxt = head[u];
edg[cnt].w = w;
edg[cnt].ids = ids;
head[u] = cnt;
}
inline void init(int n_, int m_, int root_ = 1) {
tot = 0;
cnt = 0;
n = n_;
m = m_;
root = root_;
tr = X_tree<T>(n_);
dep.assign(n_ + 1, 0);
fa.assign(n_ + 1, 0);
son.assign(n_ + 1, 0);
siz.assign(n_ + 1, 0);
top.assign(n_ + 1, 0);
head.assign(n_ + 1, 0);
edg.assign(m_ << 1 | 1, node());
dfn.assign(n_ + 1, 0);
W.assign(n_ + 1, 0);
id.assign(n_ + 1, 0);
idw.assign(n_ + 1, 0);
edge.assign(m_ + 1, 0);
}
inline void deal() {
dfs1(root, 0);
dfs2(root, root);
W[root] = 0;// 根节点赋值为0
for (int i = 1; i <= n; i++) {
upd_point(i, W[i]);
}
}
inline void dfs1(int u, int pa) {
fa[u] = pa;
siz[u] = 1;
dep[u] = dep[pa] + 1;
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to, ids = edg[i].ids;
T w = edg[i].w;
if (v == pa) {
continue;
}
W[v] = w;
edge[ids] = v;
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > siz[son[u]]) {
son[u] = v;
}
}
}
inline void dfs2(int u, int topx) {
top[u] = topx;
id[u] = ++tot;
idw[tot] = W[u];
if (son[u]) {
dfs2(son[u], topx);
} else {
return;
}
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == fa[u] || v == son[u]) {
continue;
}
dfs2(v, v);
}
}
inline int lca(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) {
swap(x, y);
}
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
inline void upd_Range(int x, int y, T k) {// 最短路径上的区间修改
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) {
swap(x, y);
}
int l = id[top[x]], r = id[x];
tr.update(1, l, r, k);
x = fa[top[x]];
}
if (dep[x] > dep[y]) {
swap(x, y);
}
int l = id[x] + 1, r = id[y];
if (l <= r) tr.update(1, l, r, k);
}
inline void upd_point(int x, T k) {// 单点修改
tr.update(1, id[x], id[x], k);
}
inline T qur_Range(int x, int y) {// 最短路径上的区间查询,查询区间之和
T ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) {
swap(x, y);
}
int l = id[top[x]], r = id[x];
ans = ans + tr.query(1, l, r).sum;
x = fa[top[x]];
}
if (dep[x] > dep[y]) {
swap(x, y);
}
int l = id[x] + 1, r = id[y];
if (l <= r) ans = ans + tr.query(1, l, r).sum;
return ans;
}
inline T qur_max(int x, int y) {// 查询区间MAX
T ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) {
swap(x, y);
}
int l = id[top[x]], r = id[x];
ans = max(ans, tr.query(1, l, r).mx);
x = fa[top[x]];
}
if (dep[x] > dep[y]) {
swap(x, y);
}
int l = id[x] + 1, r = id[y];
if (l <= r) ans = max(ans, tr.query(1, l, r).mx);
return ans;
}
};
void solve() {
int n;
std::cin >> n;
Sp<i64> tr(n, n - 1);
for (int i = 1, u, v; i < n; i++) {
i64 w = 1;
std::cin >> u >> v >> w;
tr.add(u, v, w, i);
tr.add(v, u, w, i);
}
tr.deal();
while (1) {
string opt;
std::cin >> opt;
if (opt == "CHANGE") {
int x;
i64 k;
std::cin >> x >> k;
tr.upd_point(tr.edge[x], k);
} else if (opt == "QUERY") {
int x, y;
std::cin >> x >> y;
std::cout << tr.qur_max(x, y) << '\n';
} else {
break;
}
}
}

浙公网安备 33010602011771号