#include<iostream>#include<algorithm>usingnamespacestd;constintN=1E4+10;constintV=1E7+10;constintINF=(int)0x3f3f3f3f3f3f3f3f;structEdge{intv,w;intnext;}pool[2*N];intne,head[N];voidaddEdge(intu,intv,intw){pool[++ne]={v,w,head[u]};head[u]=ne;}intn,m;intqr[N],ans[N];intvis[N];intrt;intsz[N],mxs[N]={INF};// This does what you think it doesvoidget_sz(intu,intf){sz[u]=1;for(inti=head[u];i;i=pool[i].next){intv=pool[i].v;if(v==f||vis[v])continue;get_sz(v,u);sz[u]+=sz[v];}}// This does what you think it doesvoidget_root(intu,intf,inttot){mxs[u]=0;for(inti=head[u];i;i=pool[i].next){intv=pool[i].v;if(v==f||vis[v])continue;get_root(v,u,tot);mxs[u]=max(mxs[u],sz[v]);}mxs[u]=max(mxs[u],tot-sz[u]);if(mxs[u]<mxs[rt])rt=u;}// 搭配 calc() 合并子树intf[V];intbuf1[N],buf2[N],nn1,nn2;voidget_dis(intu,intfa,intsum){if(sum>1e7)return;for(inti=1;i<=m;i++){if(sum<=qr[i]&&f[qr[i]-sum])ans[i]=1;}buf1[++nn1]=sum;for(inti=head[u];i;i=pool[i].next){intv=pool[i].v,w=pool[i].w;if(v==fa||vis[v])continue;get_dis(v,u,sum+w);}}// 处理经过 u 节点路径的贡献voidcalc(intu){f[0]=1;for(inti=head[u];i;i=pool[i].next){intv=pool[i].v,w=pool[i].w;if(vis[v])continue;get_dis(v,u,w);// 清空 bufferwhile(nn1){f[buf1[nn1]]=1;buf2[++nn2]=buf1[nn1];--nn1;}}// 撤销while(nn2){f[buf2[nn2--]]=0;}}// 点分治voidsolve(intu){calc(u);vis[u]=1;for(inti=head[u];i;i=pool[i].next){intv=pool[i].v;if(vis[v])continue;rt=0;get_sz(v,u);get_root(v,u,sz[v]);solve(rt);}}intmain(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;for(inti=1;i<=n-1;i++){intu,v,w;cin>>u>>v>>w;addEdge(u,v,w);addEdge(v,u,w);}for(inti=1;i<=m;i++){cin>>qr[i];}rt=0;get_root(1,0,n);solve(rt);for(inti=1;i<=m;i++){cout<<(ans[i]?"AYE":"NAY")<<'\n';}return0;}
路径长度最值满足点分治的使用条件。考虑 calc(u) 应该如何统计经过节点 u 的路径。我们沿用模板题的第一种思路,使用 get_dis(v) 函数合并当前子树 v 和之前的所有子树。使用一个桶数组 f 记录和 u 距离为 i 的所有节点距离 u 的最少边数。记 sum 表示 u 到当前节点 x 的路径边权和,len 表示 u 走到当前节点经过的边的数量,统计子树时时使用 f[k - sum] + len 更新答案即可。
注意边权之和 sum 是没有限制的。因此我们需要在 get_dis() 中剪掉 sum>k 的节点,否则无法存储在桶数组里。