倍增
1.求lca
2.log求k级祖先
3.lca:可以记录路径上的信息(没有修改的) 点上的和边上的都可以
并且这个信息需要容易使用倍增的形式进行合并(轻量级数据合并)
// 维护路径上边的最小值
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int h[N], ne[2 * N], e[2 * N], w[2 * N], idx;
int n, q;
void add(int a, int b, int c){
w[idx] = c, e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
const int LOGN = 20;
int depth[N], p[LOGN][N], val[LOGN][N];
void dfs(int u, int fa){
depth[u] = depth[fa] + 1;
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(j == fa) continue;
dfs(j, u);
p[0][j] = u;
val[0][j] = w[i];
}
}
void init(){
for(int i = 1; i < LOGN; i ++){
for(int j = 1; j <= n; j ++){
p[i][j] = p[i - 1][p[i - 1][j]];
val[i][j] = min(val[i - 1][j], val[i - 1][p[i - 1][j]]);
}
}
}
int query(int u, int v){
int res = 1 << 30;
if(depth[u] < depth[v]) swap(u, v);
int d = (depth[u] - depth[v]);
for(int i = 0; i < LOGN; i ++){
if(d >> i & 1){
res = min(res, val[i][u]);
u = p[i][u];
}
}
if(u == v) return res;
for(int i = LOGN - 1; i >= 0; i --){
if(p[i][u] != p[i][v]){
res = min(res, min(val[i][u], val[i][v]));
u = p[i][u]; v = p[i][v];
}
}
res = min(res, min(val[0][v], val[0][u]));
return res;
}
int main(){
scanf("%d %d", &n, &q);
memset(h, -1, sizeof h);
for(int i = 1; i <= n - 1; i ++){
int a, b, c; scanf("%d %d %d", &a, &b, &c);
add(a, b, c); add(b, a, c);
}
dfs(1, 0);
init();
while(q --){
int u, v; scanf("%d %d", &u, &v);
printf("%d\n", query(u, v));
}
}
DFS序
性质:子树标号连续
可以把子树问题转化成序列的区间问题
// 子树的点权和(区间和问题),某一个点到根的路径的权值和(每个子树加上根节点的值)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
typedef long long LL;
int h[N], ne[2 * N], e[2 * N], w[N], idx;
LL tr1[N], tr2[N];
int l[N], r[N], tot;
int n, q;
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int fa){
l[u] = ++tot;
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(j == fa) continue;
dfs(j, u);
}
r[u] = tot;
}
int lowbit(int x){
return x & -x;
}
void modify(LL tr[], int u, int x){
for(int i = u; i <= n; i += lowbit(i)) tr[i] += x;
}
LL sum(LL tr[], int x){
LL res = 0;
for(int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void init(){
for(int i = 1; i <= n; i ++){
modify(tr1, l[i], w[i]);
modify(tr2, l[i], w[i]);
modify(tr2, r[i] + 1, -w[i]);
}
}
int main(){
scanf("%d %d", &n, &q);
memset(h, -1, sizeof h);
for(int i = 1; i <= n - 1; i ++){
int a, b; scanf("%d %d", &a, &b);
add(a, b); add(b, a);
}
for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
dfs(1, 0);
init();
while(q --){
int type; scanf("%d", &type);
if(type == 1){
int x, y; scanf("%d %d", &x, &y);
int d = y - w[x]; w[x] = y;
modify(tr1, l[x], d);
modify(tr2, l[x], d);
modify(tr2, r[x] + 1, -d);
}
else if(type == 2){
int x; scanf("%d", &x);
printf("%lld %lld\n", sum(tr1, r[x]) - sum(tr1, l[x] - 1), sum(tr2, l[x]));
}
}
}
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
typedef long long LL;
typedef pair<int, int> PII;
int h[N], ne[2 * N], e[2 * N], w[N], idx;
LL tr[N];
int l[N], r[N], tot;
int n, q;
vector<PII> son[N];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int fa){
l[u] = ++tot;
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(j == fa) continue;
dfs(j, u);
son[u].push_back({l[j], r[j]});
}
r[u] = tot;
}
int lowbit(int x){
return x & -x;
}
void modify(int u, int x){
for(int i = u; i <= n; i += lowbit(i)) tr[i] += x;
}
LL sum(int x){
LL res = 0;
for(int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void init(){
for(int i = 1; i <= n; i ++){
modify(l[i], w[i]);
}
}
int main(){
scanf("%d %d", &n, &q);
memset(h, -1, sizeof h);
for(int i = 1; i <= n - 1; i ++){
int a, b; scanf("%d %d", &a, &b);
add(a, b); add(b, a);
}
for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
dfs(1, 0);
init();
int root = 1;
while(q --){
int type; scanf("%d", &type);
if(type == 1){
int x, y; scanf("%d %d", &x, &y);
int d = y - w[x]; w[x] = y;
modify(l[x], d);
}
else if(type == 2){
int x; scanf("%d", &x);
if(x == root) printf("%lld\n", sum(n));
else if(l[x] < l[root] && r[x] >= r[root]){
auto seg = *prev(upper_bound(son[x].begin(), son[x].end(), (PII){l[root], r[root]}));
printf("%lld\n", sum(n) - (sum(seg.second) - sum(seg.first - 1)));
}
else{
printf("%lld\n", sum(r[x]) - sum(l[x] - 1) );
}
}
else if(type == 3) scanf("%d", &root);
}
}
例如求解nlogn次两个点之间的距离:depth[u] + depth[v] - 2 * depth[lca]
可以使用欧拉序让lca变成log
欧拉序:dfs每次访问到的点都记录下来
u和v的lca一定是,uv在欧拉序里面之间深度最小的点,所以变成了rmq问题了,用st表搞定
括号序列:每个点访问入栈的时候左括号,出栈的时候右括号
const int LOGN = 20;
// 欧拉序的长度要开两倍
PII f[LOGN + 2][2 * N];
int l[N], r[N], depth[N], tot;
// 求欧拉序列,访问的时候加入序列,访问完一个儿子回来的时候加入序列
void dfs(int u, int fa){
l[u] = ++tot;
depth[u] = depth[fa] + 1;
f[0][tot] = {depth[u], u};
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(j == fa) continue;
dfs(j, u);
f[0][++tot] = {depth[u], u};
}
r[u] = tot;
}
// nlogn预处理出来lca
void init(){
for(int i = 1; i <= LOGN; i ++){
for(int j = 1; j + (1 << i) - 1 <= tot; j ++){
f[i][j] = min(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
}
}
}
// 求L和R的lca
if(L > R) swap(L, R);
int len = __lg(R - L + 1);
int lca = min(f[len][L], f[len][R - (1 << len) + 1]).second;