[2020多校联考]树
Solution
(没有定根就非常的离谱,后来告诉根直接就是 \(1\))
先考虑链上怎么做,显然维护一个单调栈,求出第一个比当前数大的位置,然后倍增即可。再放在树上怎么做?依旧维护单调栈,但这次不能暴力地弹掉栈顶元素了,因为这样的复杂度是假的。因为单调栈有单调性,所以直接在单调栈内二分出单调栈弹得不能再弹的位置,然后修改 \(top\),继承答案。搜索回溯的时候再将栈还原即可。对于询问从 \(u\) 到 \(v\) 初始智商为 \(w\) 能学习多少次,因只要 \(pre[u][j]\) 这个人的智商小于等于 \(w\) 就不会产生贡献,所以直接倍增地把这些点都跳过。最后不能跳了,跳到了 \(u'\)。那么 \(pre[u'][0]\) 这个点智商一定比 \(w\) 大,从这个点之后就会产生贡献了。然后再次倍增,只要还在 \(v\) 的子树里。
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 100007
inline int read(){
int x=0,flag=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
struct E{
int next,to;
}e[N<<1];
int head[N],cnt=0;
inline void add(int id,int to){
e[++cnt]=(E){head[id],to};
head[id]=cnt;
}
int sta[N],top=0,w[N],n,Q;
int pre[N][18],dep[N],ans[N];
inline bool Cmp(int x,int y){return w[x]>w[y];}
void dfs(int u,int fa_u){
int lt=top;
top=lower_bound(sta+1,sta+1+lt,u,Cmp)-sta-1;
pre[u][0]=sta[top++];
ans[u]=ans[pre[u][0]]+1;
for(int i=1;i<=17;i++)
pre[u][i]=pre[pre[u][i-1]][i-1];
int lw=sta[top],lpos=top;
sta[top]=u;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa_u) continue;
dep[v]=dep[u]+1,dfs(v,u);
}
top=lt,sta[lpos]=lw;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read(),Q=read();
for(int i=1;i<=n;i++) w[i]=read();
for(int i=2;i<=n;i++){
int u=read(),v=read();
add(u,v),add(v,u);
}
dep[1]=1,dfs(1,0);
while(Q--){
int u=read(),v=read(),c=read();
if(w[u]<=c){
for(int i=17;~i;i--)
if(pre[u][i]&&w[pre[u][i]]<=c) u=pre[u][i];
u=pre[u][0];
}
if(dep[u]<dep[v]) printf("0\n");
else{
int tmp=u;
for(int i=17;~i;i--)
if(dep[pre[u][i]]>=dep[v]) u=pre[u][i];
printf("%d\n",1+ans[tmp]-ans[u]);
}
}
}