倍增
倍增(求 \(k\) 级祖先)
for(int i = 1; i < MAXL; i++){ // 求树上 K 级祖先
for(int j = 1; j <= n; j++){ // n 为节点数量
anc[i][j] = anc[i - 1][anc[i - 1][j]];
}
}
- 查询:有两种,一种是暴力,一种是 lowbit,听到
x & (-x)
你一定想到了树状数组
for(int j = MAXL - 1; j >= 0; j--){
if(k & (1 << j)) x = anc[j][x];
}
for(int i = 1, cnt = 0; i < MAXN; i *= 2, cnt++) Log[i] = cnt;
while(k > 0){
int u = k & (-k);
x = anc[Log[u]][x];
k -= u;
}
LCA思想(求 \(x\) 和 \(y\) 的最近公共祖先)
不利用倍增的LCA
- 不断上升 \(x\) 或 \(y\)
- 当 \(x\) 比 \(y\) 深,上升 \(x\)
- 当 \(y\) 比 \(x\) 深,上升 \(y\)
利用倍增的LCA
- 先使 \(x\) 和 \(y\) 的深度相同(只需要上升更加深的,可以将
abs(dep[x] - dep[y])
看做 \(k\) 做倍增)
- 第一种做法:二分 \(x\) 和 \(y\) 同时上升多少在同一点上(mid大于答案,所有mid都满足)
- 第二种做法如下:
int Lca(int x, int y){ // 求 x 和 y 的最近公共祖先
if(dep[x] > dep[y]) swap(x, y);
y = Find(y, dep[y] - dep[x]); // 平衡 x 和 y 的深度
if(y == x) return x;
for(int j = MAXL - 1; j >= 0; j--){ // 可以利用第一种做法发现的性质
if(anc[j][x] != anc[j][y]){
x = anc[j][x], y = anc[j][y];
}
}
return anc[0][y];
}
点击 luogu LCA 模板题树链剖分代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int MAXN = 5e5 + 3;
int n, m, root;
int etot = 0, estart[MAXN], eg[MAXN * 2], enxt[MAXN * 2], etop[MAXN]; // 链式前向星
int fa[MAXN], sz[MAXN], dep[MAXN], son[MAXN]; // 父亲、子树大小、深度、重儿子
int top[MAXN]; // 所在重链的顶点
void ADDeg(int U, int V){
etot++, estart[U] = (estart[U] == 0 ? etot : estart[U]);
enxt[etop[U]] = etot, etop[U] = etot;
eg[etot] = V;
}
void mdfs1(int x, int dad){ // 求出:父亲、子树大小、深度、重儿子
fa[x] = dad, dep[x] = dep[dad] + 1, sz[x] = 1, son[x] = 0;
for(int e = estart[x], nxt = eg[e]; e > 0; e = enxt[e], nxt = eg[e]){
if(nxt == dad) continue;
mdfs1(nxt, x), sz[x] += sz[nxt];
if(son[x] == 0 || sz[son[x]] < sz[nxt]) son[x] = nxt;
}
}
void mdfs2(int x, int ntop){ // 求出:所在重链的顶点
top[x] = ntop;
if(son[x] > 0) mdfs2(son[x], ntop);
for(int e = estart[x], nxt = eg[e]; e > 0; e = enxt[e], nxt = eg[e]){
if(nxt != fa[x] && nxt != son[x]) mdfs2(nxt, nxt);
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> root;
for(int i = 1, U, V; i < n; i++){
cin >> U >> V, ADDeg(U, V), ADDeg(V, U);
}
mdfs1(root, 0), mdfs2(root, root);
for(int sb = 1, X, Y; sb <= m; sb++){
cin >> X >> Y;
while(top[X] != top[Y]){
if(dep[top[X]] < dep[top[Y]]) swap(X, Y);
X = fa[top[X]];
}
cout << (dep[X] > dep[Y] ? Y : X) << "\n";
}
return 0;
}
点击查看 luogu 树链剖分模板题代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int MAXN = 1e5 + 3;
struct Segment_Tree{
LL sum, lz;
}egtr[MAXN * 4];
LL egopt, egret;
int n, m, root;
LL mod, a[MAXN];
int etot = 0, estart[MAXN], eg[MAXN * 2], enxt[MAXN * 2], etop[MAXN]; // 链式前向星,卡常不得不防
int fa[MAXN], sz[MAXN], dep[MAXN], son[MAXN]; // 父亲、子树大小、深度、重儿子
int top[MAXN], dfntop = 0, dfn[MAXN], mxdfn[MAXN]; // 所在重链的顶点、dfs序上的编号、子树内的最大编号
inline void ADDeg(int U, int V){
etot++, estart[U] = (estart[U] == 0 ? etot : estart[U]);
enxt[etop[U]] = etot, etop[U] = etot;
eg[etot] = V;
}
void mdfs1(int x, int dad){ // 求出:父亲、子树大小、深度、重儿子
fa[x] = dad, dep[x] = dep[dad] + 1, sz[x] = 1, son[x] = 0;
for(int e = estart[x], nxt = eg[e]; e > 0; e = enxt[e], nxt = eg[e]){
if(nxt == dad) continue;
mdfs1(nxt, x), sz[x] += sz[nxt];
if(son[x] == 0 || sz[son[x]] < sz[nxt]) son[x] = nxt;
}
}
void mdfs2(int x, int ntop){ // 求出:所在重链的顶点、dfs序上的编号
top[x] = ntop, dfn[x] = ++dfntop;
if(son[x] > 0) mdfs2(son[x], ntop);
for(int e = estart[x], nxt = eg[e]; e > 0; e = enxt[e], nxt = eg[e]){
if(nxt != fa[x] && nxt != son[x]) mdfs2(nxt, nxt);
}
mxdfn[x] = dfntop;
}
void Downlz(int i, int l, int r, LL lz){ // 下传懒惰标记
egtr[i].lz += lz, egtr[i].sum += lz * (r - l + 1);
}
void S(int i, int l, int r, int tl, int tr){ // 区间修改+区间查询 线段树
if(l == tl && r == tr){
if(egopt == 1){
egtr[i].lz += egret, egtr[i].sum += egret * (r - l + 1);
}else{
egret += egtr[i].sum;
}
return;
}
int mid = (l + r) >> 1;
Downlz(i * 2, l, mid, egtr[i].lz), Downlz(i * 2 + 1, mid + 1, r, egtr[i].lz);
egtr[i].lz = 0;
if(tl <= mid){
S(i * 2, l, mid, tl, min(tr, mid));
}
if(mid + 1 <= tr){
S(i * 2 + 1, mid + 1, r, max(tl, mid + 1), tr);
}
egtr[i].sum = egtr[i * 2].sum + egtr[i * 2 + 1].sum;
}
int main(){
ios::sync_with_stdio(0), cin.tie(0);
//freopen("P3384_8.in", "r", stdin);
//freopen("P3384_.out", "w", stdout);
cin >> n >> m >> root >> mod;
for(int i = 1; i <= n; i++) cin >> a[i], a[i] %= mod;
for(int i = 1, U, V; i < n; i++) cin >> U >> V, ADDeg(U, V), ADDeg(V, U);
mdfs1(root, 0), mdfs2(root, root);
for(int i = 1; i <= n; i++){ // 初始化
egopt = 1, egret = a[i], S(1, 1, n, dfn[i], dfn[i]);
}
for(int sb = 1, op, X, Y; sb <= m; sb++){
cin >> op >> X;
LL ans = 0;
if(op == 1){
cin >> Y >> egret, egopt = 1;
while(top[X] != top[Y]){ // 修改路径
if(dep[top[X]] < dep[top[Y]]) swap(X, Y);
S(1, 1, n, dfn[top[X]], dfn[X]);
X = fa[top[X]];
}
if(dep[X] < dep[Y]) swap(X, Y);
S(1, 1, n, dfn[Y], dfn[X]);
}else if(op == 2){
cin >> Y, egopt = 2, egret = 0;
while(top[X] != top[Y]){ // 查询路径
if(dep[top[X]] < dep[top[Y]]) swap(X, Y);
S(1, 1, n, dfn[top[X]], dfn[X]);
X = fa[top[X]];
}
if(dep[X] < dep[Y]) swap(X, Y);
S(1, 1, n, dfn[Y], dfn[X]);
cout << egret % mod << "\n"; // 输出 ans
}else if(op == 3){
cin >> egret, egopt = 1;
S(1, 1, n, dfn[X], mxdfn[X]); // 修改子树
}else if(op == 4){
egopt = 2, egret = 0, S(1, 1, n, dfn[X], mxdfn[X]);
cout << egret % mod << "\n";
}else cout << "1145141919810\n";
}
return 0;
}