题目链接:https://ac.nowcoder.com/acm/problem/13331
来份在线查询的代码,先说这道题,在第一遍dfs求重儿子的时候,顺便处理数组dp[maxn],其中dp[u]为从节点u到根节点1有几次购买操作,如何求答案呢?

假设有两个点x和y,其中y----是x的某一级祖先,而且y的值是从x到根节点路径上第一个比x大的节点,很显然,dp[x]=dp[y]+1。可以配合线段树维护实现查询第一个比x大的节点位置的操作。

本题目查询节点u和v,其中v保证是u的祖先节点。
对于u到v的路径上,我们找到第一个要购买操作的节点位置------假设为u1。
对于u到v的路径上,我们找到最后一个要购买操作的节点位置------假设为v1。

对于这条路径u---> u1(第一次购买操作) ---> v1(最后一次购买操作) ---> v
当然,存在u和u1是同一个位置这种类似情况,就是u和u1和v1和v不一定是不同的点,对我们的解题不构成影响

我们可以发现,u1肯定是第一个比出发是所携带的价值c大的节点,v1肯定是路径u->v中值最大且最靠近u的,因为第一次遇到就换,这很显然。
所以,起点到节点u1无操作,节点v1到v无操作,因此这两段路径我们可以不管,那么答案就是dp[u1]-dp[v1]+1,这就解决了。

所以,我们可以先树剖,然后线段树维护区间最值,深度越大越优先,并且设置id标记,标记最大值的下标,方便我们定位到符合要求的节点,对于查询u到v,找出u1和v1即可,2个查询函数就行。

重头戏来了,这题有个限制——v是u的祖先,很多人和我想法一定一样,不满足只解决有这个限制的题目,对于没有这个限制的题目,这种情况我们怎么解决,上面的方法还是可以进一步解决这个问题,我们已经用一个数组dp,对于dp[idx]表示了idx节点到根的路径上的操作次数。

我们可以增加一个数组rev_dp[maxn],rev_dp[idx]维护的是对于从根节点走到节点idx的购买操作次数,和dp数组正好相反,怎么求呢,我们看啊,假设两个节点x和y,y是x的祖先且y是根节点到x节点路径的最大值且最靠近根节点,那么rev_dp[x]=rev_dp[y]+( val[x] > val[y] ? 1:0 )

我们可以先处理节点u到lca(u,v)的答案,保存了最大值MAX,得到sum1,和本题一样的处理得到的答案,再处理lca(u,v)到v的路径,我们只需要找到lca(u,v)到v路径的第一个值比MAX大的节点k1,以及路径中值最大且离v最远的节点k2,得到sum2=rev_dp[k2]-rev_dp[k1]+1,
答案就是sum1+sum2,这是大概思路,细节用线段树维护即可实现。
处理路径的时候用树剖,复杂度也满足要求。

这里给出思路,代码是解决该题的代码。

#include <cctype>
#include <cfloat>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <istream>
#include <iterator>
#include <list>
#include <map>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#define PI 3.1415926535898
using namespace std;
const int maxn=1e5+10;
struct edge {
    int to,Next;
}Edge[maxn<<1];
int f[maxn],val[maxn],Son[maxn],Top[maxn],sz[maxn],dep[maxn],Head[maxn],tot,dp[maxn];
void Add(int from, int to) {
    Edge[tot]={to,Head[from]};
    Head[from]=tot++;
    Edge[tot]={from,Head[to]};
    Head[to]=tot++;
}
struct node {
    int Left,Right,val,id;
}NODE[maxn<<2];
int pos[maxn];
void PushUp(int idx) {
    NODE[idx].val=max(NODE[idx<<1].val,NODE[idx<<1|1].val);
    if (NODE[idx].val==NODE[idx<<1|1].val) NODE[idx].id=NODE[idx<<1|1].id;
    else NODE[idx].id=NODE[idx<<1].id;
}
void build(int l, int r, int idx) {
    NODE[idx].Left=l,NODE[idx].Right=r;
    NODE[idx].val=0;
    if (l==r) {
        NODE[idx].val=val[pos[l]];
        NODE[idx].id=l;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,idx<<1);
    build(m+1,r,idx<<1|1);
    PushUp(idx);
}
void Updata(int pos, int idx, int c) {
    if (NODE[idx].Left==NODE[idx].Right) {
        NODE[idx].val+=c;
        return;
    }
    int m=(NODE[idx].Left+NODE[idx].Right)>>1;
    if (pos<=m) Updata(pos,idx<<1,c);
    else Updata(pos,idx<<1|1,c);
    PushUp(idx);
}
int query_Max(int l, int r, int idx, int c) {
    if (l>r) return -1;
    if (NODE[idx].Left==l&&r==NODE[idx].Right) {
        if (NODE[idx].val>c) return NODE[idx].id;
        else return -1;
    }
    int m=(NODE[idx].Left+NODE[idx].Right)>>1;
    if (r<=m) return query_Max(l,r,idx<<1,c);
    else if (l>m) return query_Max(l,r,idx<<1|1,c);
    else {
        int L=query_Max(l,m,idx<<1,c);
        int R=query_Max(m+1,r,idx<<1|1,c);
        if (L==-1) return R;
        if (R==-1) return L;
        if (val[pos[R]]>=val[pos[L]]) return R;
        return L;
    }
}
int query(int l, int r, int idx, int c) {
    if (l>r) return -1;
    if (NODE[idx].Left==l&&r==NODE[idx].Right) {
        if (NODE[idx].val<=c) return -1;
        if (l==r) {
            return l;
        } else {
            int m=(l+r)>>1;
            if (NODE[idx<<1|1].val>c) return query(m+1,r,idx<<1|1,c);
            else return query(l,m,idx<<1,c);
        }
    }
    int m=(NODE[idx].Left+NODE[idx].Right)>>1;
    if (r<=m) return query(l,r,idx<<1,c);
    else if (l>m) return query(l,r,idx<<1|1,c);
    else {
        int L=query(l,m,idx<<1,c);
        int R=query(m+1,r,idx<<1|1,c);
        if (R!=-1) return R;
        else return L;
    }
}
int qu[maxn];
void dfs(int u, int fa) {
    sz[u]=1;
    f[u]=fa;
    dep[u]=dep[fa]+1;
    int idx=query(1,dep[u]-1,1,val[u]);
    if (idx==-1) dp[u]=1;
    else dp[u]=dp[qu[idx]]+1;
    Updata(dep[u],1,val[u]);
    qu[dep[u]]=u;
    int Mx=0;
    for (int i=Head[u]; ~i; i=Edge[i].Next) {
        int v=Edge[i].to;
        if (v==fa) continue;
        dfs(v,u);
        sz[u]+=sz[v]; 
        if (Mx<sz[v]) {
            Son[u]=v;
            Mx=sz[v];
        }
    }
    Updata(dep[u],1,-val[u]);
}
int dfn[maxn],cnt;
void solve(int u, int top) {
    Top[u]=top;
    dfn[u]=++cnt;
    pos[cnt]=u;
    if (Son[u]==0) return;
    solve(Son[u],top);
    for (int i=Head[u]; ~i; i=Edge[i].Next) {
        int v=Edge[i].to;
        if (v==f[u]||v==Son[u]) continue;
        solve(v,v);
    }
}
int main() {
    int n,m;
    memset(Head,-1,sizeof(Head));
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++) scanf("%d",&val[i]);
    for (int i=1; i<n; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        Add(a,b);
    }
    build(1,n,1);
    dfs(1,0);
    solve(1,1);
    build(1,n,1);
    while (m--) {
        int u,v,c,res=0;
        scanf("%d%d%d",&u,&v,&c);
        int Max_id=-1,Max=c,first_Max=-1;
        while (Top[u]!=Top[v]) {
            if (first_Max==-1) {
                int idx=query(dfn[Top[u]],dfn[u],1,c);
                if (idx!=-1) {
                    res=dp[pos[idx]];
                    first_Max=val[pos[idx]];
                }
            }
            int idx1=query_Max(dfn[Top[u]],dfn[u],1,Max);
            if (idx1!=-1) {
                Max_id=pos[idx1];
                Max=val[pos[idx1]];
            }
            u=f[Top[u]];
        }
        if (first_Max==-1) {
            int idx=query(dfn[v],dfn[u],1,c);
            if (idx!=-1) {
                res=dp[pos[idx]];
                first_Max=val[pos[idx]];
            }
//            cout<<"idx="<<idx<<endl;
        }
        int idx1=query_Max(dfn[v],dfn[u],1,Max);
//        cout<<"idx1="<<val[pos[idx1]]<<endl;
        if (idx1!=-1) {
            Max_id=pos[idx1];
            Max=val[pos[idx1]];
        }
        if (first_Max!=-1) res-=(dp[Max_id]-1);
//        cout<<first_Max<<endl;
        printf("%d\n",res);
    }
    return 0;
}