// 直接递归版本(不推荐,栈深可能爆)
long long power(long long a, long long b) {
if (b == 0) return 1;
long long half = power(a, b / 2);
if (b % 2 == 0) return half * half;
else return half * half * a;
}
2.3 示例 2:模幂(适合大数、取模)
cpp复制代码
using int64 = long long;
const int MOD = 1e9+7;
// 循环实现
int64 mod_pow(int64 a, int64 b, int mod = MOD) {
int64 res = 1;
a %= mod;
while (b > 0) {
if (b & 1) res = (res * a) % mod; // 选bit:乘基
a = (a * a) % mod; // 平方基
b >>= 1; // 右移
}
return res;
}
struct Mat {
long long a, b, c, d; // [a b; c d]
Mat(long long a_=1, long long b_=0, long long c_=0, long long d_=1) : a(a_), b(b_), c(c_), d(d_) {}
};
Mat mul(const Mat &x, const Mat &y) {
return Mat(
(x.a * y.a + x.b * y.c) % MOD,
(x.a * y.b + x.b * y.d) % MOD,
(x.c * y.a + x.d * y.c) % MOD,
(x.c * y.b + x.d * y.d) % MOD
);
}
Mat mat_pow(Mat base, long long exp) {
Mat res; // 单位矩阵
while (exp) {
if (exp & 1) res = mul(res, base);
base = mul(base, base);
exp >>= 1;
}
return res;
}
典型应用 :斐波那契数列:
Fn=(1110) n−1⋅(10)Fn=(1110)n−1⋅(10)
3. 二进制提升(Binary Lifting)
3.1 场景
在树(Parent‑Child 关系)上进行"祖先查询"是最常见的场景,尤其是:
LCA(Lowest Common Ancestor):求两点最近公共祖先
k‑th ancestor:查询某点向上 k 步的节点
边权累加 / 距离:可以搭配二进制提升做区间求和
3.2 思路概述
预处理 :对于每个节点 v,记录:
up[v][k]=the ancestor of v that is 2k steps above.up[v][k]=the ancestor of v that is 2k steps above.
up[v][0] 就是父节点(或者自己)。
查询 :把 k (或者某个 LCA 候选) 写成二进制,按位"跳跃",每跳一次就把 k 减少相应的 2 的权。
举例:要查询 13(1101₂) 步上节点:
先跳 8 步 (up[v][3]),
再跳 4 步 (up[ up[v][3] ][2]),
再跳 1 步 (up[ up[ up[v][3] ][2] ][0])。
LCA:先把两点提升到相同深度,再同时往上跳,直到祖先相同。
时间复杂度:
预处理:O(NlogN)O(NlogN)
查询:O(logN)O(logN)
3.3 示例 1:构建树 + 预处理
cpp复制代码
const int MAXN = 200005;
const int LOG = 20; // 因为 2^19 > 200k
vector<int> g[MAXN];
int up[MAXN][LOG];
int depth[MAXN];
void dfs(int v, int p){
up[v][0] = p; // 第一层祖先
depth[v] = depth[p] + 1; // 深度
for(int k = 1; k < LOG; ++k){
up[v][k] = up[ up[v][k-1] ][k-1]; // 2^k 祖先
}
for(int to : g[v]){
if(to == p) continue;
dfs(to, v);
}
}
使用提醒:
根点的父节点设为 0(或自身),并且 depth[0] = -1,方便计算深度。
up[v][k] 对于 k 过大时会回到 0,但不会影响后续查询。
3.4 示例 2:k‑th ancestor
cpp复制代码
int kth_ancestor(int v, int k){
for(int i = 0; i < LOG; ++i){
if(k & (1 << i)){
v = up[v][i];
if(v == 0) break; // 超出根,返回 0
}
}
return v; // 0 表示不存在
}
3.5 示例 3:LCA(离线+在线)
在线版(查询时随时得到答案):
cpp复制代码
int lca(int u, int v){
if(depth[u] < depth[v]) swap(u, v);
// 把 u 提升到 v 的深度
u = kth_ancestor(u, depth[u] - depth[v]);
if(u == v) return u;
for(int i = LOG-1; i >= 0; --i){
if(up[u][i] != up[v][i]){
u = up[u][i];
v = up[v][i];
}
}
return up[u][0]; // 现在 u 和 v 的父亲相同,为 LCA
}
/**
* @file binary_lifting.hpp
* @brief C++ template for binary lifting (LCA, kth ancestor, distance)
* All functions are O(log N) after O(N log N) preprocessing.
*
* @note
* - Root is assumed to be `1` and its parent is set to `0`.
* - depth[0] is set to -1 to make depth[1]==0.
* - `LOG` should be set according to maximum node number.
*
* @example
* // Tree input
* cin >> n;
* for (int i = 0; i < n-1; ++i) {
* int u, v; cin >> u >> v;
* add_edge(u, v);
* }
* // Preprocessing
* depth[0] = -1; // prepare for dfs(1, 0);
* dfs(1, 0);
*
* // Queries
* cout << lca(4, 7) << endl; // nearest common ancestor
* cout << kth_ancestor(8, 3) << endl; // ancestor 3 levels up
* cout << dist(4, 7) << endl; // distance between nodes
*/