【倍增思想】

倍增经典递推公式:f[u][i]=f[f[u][i−1]][i−1]
即: u的第2^i个父亲节点u的第2^(i-1)个父亲的节点的第2^(i-1)个父亲节点
因为:2^i = 2^(i-1)+2^(i-1)

【本题】


在这题中,题目说是树上的多次查询,可以联想到倍增(优化暴力查询),如果不是多次查询,倍增并不好用,因为倍增需要预处理f数组以及树的深度
定义f[u][i]为从某节点u出发往根走,购买了2^i件物品的位置
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100005
int p[MAXN],h[MAXN],ne[MAXN];//链式前向星写法
int num=0;
const int N = 2e5 + 7;
int n, q, val[N], mrk[N];
int f[N][20], dep[N];
void addEdge(int from, int to){
    p[++num] = to;//to->from
    ne[num] = h[from];
    h[from] = num;
 
    p[++num] = from;
    ne[num] = h[to];
    h[to]=num;
}
//f[u][i]:保存u结点往根方向第2^i个val大于它的结点
void dfs(int u, int father){
    dep[u] = dep[father] + 1;//记录深度
    if (val[u] < val[father]){
        f[u][0] = father;
    }else{
        int x = father;
        for (int i = 19; i >= 0; --i){//x逼近第一个比val[u]大的结点下方的结点
            if (f[x][i] && val[u] >= val[f[x][i]]){
                x = f[x][i];
            }
        }
        f[u][0] = f[x][0];//得到第一个比val[u]大的祖先结点
    }
    for (int i = 1; i <= 19; ++i){//刷新倍增数组
        f[u][i] = f[f[u][i-1]][i-1];
    }
    for(int i=h[u];i!=0;i=ne[i])if(p[i]!=father){
       // f[p[i]][0]=u;
        dfs(p[i],u);
    }
}
int main(void){
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) cin >> val[i];
    for (int i = 0; i < n-1; ++i){
        int u, v; cin >> u >> v;
        addEdge(u,v);
    }
    for (int i = 1; i <= q; ++i){
        int u, v, c; cin >> u >> v >> c;
        addEdge(n+i,u);//每个询问新加一个结点连在u下面
        val[n+i] = c, mrk[i] = v;//新结点val设为c,记录目标祖先v
    }
    dep[0]=0;
    dfs(1, 0);//dfs预处理倍增数组
    for (int i = 1; i <= q; ++i){
        int u = n+i, v = mrk[i], ans = 0;
        for (int j = 19; j >= 0; --j){
            if (dep[f[u][j]] >= dep[v]){//用倍增数组,从u往v跳
                u = f[u][j];
                ans += (1<<j);//记录跳了多少步
            }
        }
        cout << ans << endl;
    }
    return 0;
}


【再贴一道ST表,理解倍增思想】

链接:https://ac.nowcoder.com/acm/contest/82/B

给你一个长为n的序列a和一个常数k

有m次询问,每次查询一个区间[l,r]内所有数最少分成多少个连续段,使得每段的和都 <= k

如果这一次查询无解,输出"Chtholly"

输入描述:

第一行三个数n,m,k
第二行n个数表示这个序列a
之后m行,每行给出两个数l r表示一次询问

输出描述:

输出m行,每行一个整数,表示答案
示例1

输入

复制 5 5 7 2 3 2 3 4 3 3 4 4 5 5 1 5 2 4
5 5 7
2 3 2 3 4
3 3
4 4
5 5
1 5
2 4

输出

复制 1 1 1 2 2
1
1
1
2
2