题目:

有一个树状的城市网络(即 n 个城市由 n-1 条道路连接的连通图),首都为 1 号城市,每个城市售卖价值为 a_i 的珠宝。
你是一个珠宝商,现在安排有 q 次行程,每次行程为从 u 号城市前往 v 号城市(走最短路径),保证 v 在 u 前往首都的最短路径上。 在每次行程开始时,你手上有价值为 c 的珠宝(每次行程可能不同),并且每经过一个城市时(包括 u 和 v ),假如那个城市中售卖的珠宝比你现在手上的每一种珠宝都要优秀(价值更高,即严格大于),那么你就会选择购入。
现在你想要对每一次行程,求出会进行多少次购买事件。


做法:

下手慢了,数据更新了,暴力跑不动了。/(ㄒoㄒ)/~~









代码:

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define debug(a) cout << #a ": " << a << endl
using namespace std;
const int N = 2e5 + 7;
int n, q, val[N], mrk[N];
vector<int> g[N];
int fa[N][20], dep[N];
//fa[u][i]:保存u结点往根方向第2^i个val大于它的结点
void dfs(int u, int p){
    dep[u] = dep[p] + 1;//记录深度
    if (val[u] < val[p]){
        fa[u][0] = p;//若父节点p的val比u的val大,fa[u][0] = p
    }else{
        int x = p;
        for (int i = 19; i >= 0; --i){//x逼近第一个比val[u]大的结点下方的结点
            if (fa[x][i] && val[u] >= val[fa[x][i]]){
                x = fa[x][i];
            }
        }
        fa[u][0] = fa[x][0];//得到第一个比val[u]的的祖先结点
    }
    for (int i = 1; i <= 19; ++i){//倍增数组,递推
        fa[u][i] = fa[fa[u][i-1]][i-1];
    }
    for (int i = 0; i < g[u].size(); ++i){
        int v = g[u][i];
        if (v == p) continue;
        dfs(v, u);
    }
}
int main(void){
    IOS;
    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;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    for (int i = 1; i <= q; ++i){
        int u, v, c; cin >> u >> v >> c;
        g[n+i].push_back(u);//每个询问新加一个结点连在u下面
        g[u].push_back(n+i);
        val[n+i] = c, mrk[i] = v;//新结点val设为c,记录目标祖先v
    }
    dfs(1, 0);//dfs预处理倍增数组
    for (int i = 1; i <= q; ++i){
        int u = n+i, v = mrk[i], ans = 0;
        for (int i = 19; i >= 0; --i){
            if (dep[fa[u][i]] >= dep[v]){//用倍增数组,从u往v跳
                u = fa[u][i];
                ans += (1<<i);//记录跳了多少步
            }
        }
        cout << ans << endl;
    }
    return 0;
}