首先容易计算出每个点的 \(f\)。
一种暴力的做法是每次将两端的 \(f\) 值的差 \(\le lim\) 的边存下来排序,双指针+\(LCT\)(线段树分治),但是常数很大,过不了。
巧妙的做法是以 \(f\) 值最小的点为根建树,这个点必然在直径上,并且每个节点的 \(f\) 都不会小于父亲节点的 \(f\),于是就可以对于每个点二分一个最浅的祖先,那么以 祖先 \(\rightarrow\) 当前点 路径上的节点的 \(f\) 为最小值时的答案就 \(+1\),这个可以差分,然后就做完了。
Ps: 求 \(f\) 可以不用换根 \(dp\),直接找直径端点求就行((
\(\texttt{Code:}\)
#include<bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
#define ll long long
using namespace std;
const int maxn=1e5+5;
struct Edge{int to,dis,nxt;}E[maxn<<1];
int cnt,head[maxn];
void addedg(int u,int v,int w){E[++cnt]=(Edge){v,w,head[u]};head[u]=cnt;}
int n,df[maxn],sn[maxn];
ll f[maxn][3],g[maxn];
void dfs1(int u,int fa){
for(int i=head[u];i;i=E[i].nxt){
int v=E[i].to;
if(v==fa)continue;
df[v]=E[i].dis;dfs1(v,u);
if(f[v][0]+E[i].dis>f[u][0]){
sn[u]=v;f[u][1]=f[u][0];f[u][0]=f[v][0]+E[i].dis;
}else f[u][1]=max(f[u][1],f[v][0]+E[i].dis);
}
}
void dfs2(int u,int fa){
if(fa){
if(sn[fa]==u)f[u][2]=max(f[fa][1],f[fa][2])+df[u];
else f[u][2]=max(f[fa][0],f[fa][2])+df[u];
}
for(int i=head[u];i;i=E[i].nxt){
int v=E[i].to;
if(v==fa)continue;
dfs2(v,u);
}
}
int sb[maxn],fa[maxn],tag[maxn],ans;
void dfs(int u,int fath,int depth,ll lim){
sb[depth]=u;fa[u]=fath;
int l=0,r=depth;
while(l+1<r){
int mid=(l+r)>>1;
if(g[u]-g[sb[mid]]<=lim)r=mid;else l=mid;
}tag[u]++;tag[fa[sb[r]]]--;
for(int i=head[u];i;i=E[i].nxt){
int v=E[i].to;
if(v==fath)continue;
dfs(v,u,depth+1,lim);
}
}
void query(int u){
for(int i=head[u];i;i=E[i].nxt){
int v=E[i].to;
if(v==fa[u])continue;
query(v);tag[u]+=tag[v];
}ans=max(ans,tag[u]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
addedg(u,v,w);addedg(v,u,w);
}dfs1(1,0);dfs2(1,0);
for(int i=1;i<=n;i++)g[i]=max(f[i][0],f[i][2]);
int rt=0;g[0]=1e12;
for(int i=1;i<=n;i++)if(g[i]<g[rt])rt=i;
int q;scanf("%d",&q);
while(q--){
ll lim;scanf("%lld",&lim);
for(int i=1;i<=n;i++)tag[i]=0;
dfs(rt,0,1,lim);
ans=0;query(rt);
printf("%d\n",ans);
}
return 0;
}