不妨假设边的长度为 1 1 1,然后允许小数的出现。设 ( i , e ) (i,e) (i,e)表示从 i i i出发,油量为 e e e的状态。发现对于点 i i i,如果剩余能走的距离 e ≤ K 2 e\le \frac{K}{2} e≤2K,并且到最近的关键点的距离 ≤ e \le e ≤e,事实上等价于从这个关键点出发。
基于上述这个事实,我们可以从每个关键点出发跑多源 B F S BFS BFS,然后处理出关键点之间的连通性,这可以用并查集维护。设从 u u u往 u → v u\to v u→v路径上走 K 2 \frac{K}{2} 2K到达的点为 u ′ u' u′,可以证明从 u u u出发, e = K e=K e=K能到达的有用的关键点和从 u ′ u' u′出发, e = K 2 e=\frac{K}{2} e=2K能走到的关键点的集合相同。这样再套用前面的结论就做完了。但是如何证明呢?
让我们理论分析一波。设从 u u u到 v v v的路径中走了 x x x步,每个点到补给站的最短距离为 d i d_i di,那么如果这个点对应的补给站有用的话应该满足 K − x ∈ [ d i , K − d i ] K-x\in [d_i,K-d_i] K−x∈[di,K−di],因此我们有 d i ≤ K 2 d_i\le \frac{K}{2} di≤2K。观察这个式子 ,如果 x ≤ K 2 x\le \frac{K}{2} x≤2K那么 d i ≤ x d_i\le x di≤x,因此从 u ′ u' u′到关键点的距离为 K 2 − x + d i ≤ K 2 \frac{K}{2}-x+d_i\le \frac{K}{2} 2K−x+di≤2K;如果 x > K 2 x> \frac{K}{2} x>2K那么 K − x ≥ d i K-x\ge d_i K−x≥di,因此从 u u u到关键点的距离为 x − K 2 + d i ≤ K 2 x-\frac{K}{2}+d_i\le \frac{K}{2} x−2K+di≤2K。这样就证完了。个人还是比较喜欢这样的代数证明的。
为避免出现 0.5 0.5 0.5的情况,可以考虑在每条边中间再添加一个虚点。
对于跨 L C A LCA LCA的情况,有一个非常巧妙的 trick \text{trick} trick可以学一下:如果到 L C A LCA LCA的距离 ≥ K 2 \ge \frac{K}{2} ≥2K那么相当于从这个点跳 K 2 \frac{K}{2} 2K的距离,如果 < K 2 <\frac{K}{2} <2K那么相当于从另一个点跳 dist(u,v) − K 2 \text{dist(u,v)}-\frac{K}{2} dist(u,v)−2K的距离。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define ll long long
#define db double
#define inf 0x3f3f3f3f
using namespace std;
const int N=4e5+5;
int n,K,m,Q,f[N][20],dep[N],fa[N],home[N];
vector<int>g[N];
void add(int x,int y){
g[x].pb(y),g[y].pb(x);
}
queue<pair<int,int>>q;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unionset(int x,int y){
int u=find(x),v=find(y);
if(u!=v)fa[u]=v;
}
int get(int x,int y){
for(int i=19;i>=0;i--){
if(y>>i&1)x=f[x][i];
}
return x;
}
int Lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=19;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
if(x==y)return x;
for(int i=19;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
void dfs(int u,int topf){
dep[u]=dep[topf]+1,f[u][0]=topf;
for(int i=1;i<=19;i++)f[u][i]=f[f[u][i-1]][i-1];
for(auto v:g[u]){
if(v!=topf){
dfs(v,u);
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>K>>m;for(int i=1;i<2*n;i++)fa[i]=i;
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y,add(x,n+i),add(n+i,y);
}
for(int i=1;i<=m;i++){
int x;cin>>x,home[x]=x,q.push({x,K});
}
while(q.size()){
int u=q.front().fi,step=q.front().se;q.pop();
if(!step)continue;
for(auto v:g[u]){
if(!home[v]){
home[v]=home[u],q.push({v,step-1});
unionset(v,home[u]);
}
else {
unionset(home[u],home[v]);
}
}
}
dfs(1,0);
cin>>Q;
for(int i=1;i<=Q;i++){
int u,v,u2,v2;cin>>u>>v;
int lca=Lca(u,v);
if(dep[u]+dep[v]-2*dep[lca]<=2*K){
cout<<"YES"<<"\n";
}
else{
if(dep[u]-dep[lca]>=K)u2=get(u,K);
else u2=get(v,dep[u]+dep[v]-2*dep[lca]-K);
if(dep[v]-dep[lca]>=K)v2=get(v,K);
else v2=get(u,dep[u]+dep[v]-2*dep[lca]-K);
if(find(u2)==find(v2))cout<<"YES"<<"\n";
else cout<<"NO"<<"\n";
}
}
}