這是一道擁有 *3400 標籤的題目。
首先很顯然可以將題意中的條件轉化為任意兩個度數為一的節點都能透過不超過兩條路徑互相到達。接下來隨便取一個度數大於一的節點作為根,如果 \(n=2\) 直接判掉即可。
考慮兩個葉子節點能互相到達一定需要滿足什麼條件,發現兩個點透過一條路徑能到達的最淺的節點一定是祖先關係,於是所有葉子節點能到達的最淺的節點就一定在一條鏈上。
然後發現答案為 YES
當且僅當每個葉子節點都能透過一條路徑到達這條鏈上深度最深的節點,直接判就做完了。時間複雜度 \(\mathcal O(n\log n)\)。
參考程式碼:
#include<bits/stdc++.h>
#define mxn 500003
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
int t,n,m,rt,ps,mx,pt,tot,d[mxn],dfn[mxn],rs[mxn],f[mxn][20];
vector<int>g[mxn],e[mxn];
void dfs(int x,int fa){
dfn[x]=++tot;
d[x]=d[fa]+1,f[x][0]=fa;
rept(i,1,19)f[x][i]=f[f[x][i-1]][i-1];
for(int i:g[x])if(i!=fa){
dfs(i,x);
}
rs[x]=tot;
}
int lca(int x,int y){
if(d[x]<d[y])swap(x,y);
drep(i,18,0)if(d[f[x][i]]>=d[y])x=f[x][i];
if(x==y)return x;
drep(i,18,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
inline bool in(int x,int y){
return dfn[x]<=dfn[y]&&dfn[y]<=rs[x];
}
signed main(){
t=read();
while(t--){
n=read(),m=read();
rep(i,1,n)g[i].clear(),e[i].clear();
for(int i=1,x,y;i<n;++i){
x=read(),y=read();
g[x].pb(y),g[y].pb(x);
}
for(int i=0,x,y;i<m;++i){
x=read(),y=read();
if(x==y)continue;
if(g[x].size()==1)e[x].pb(y);
if(g[y].size()==1)e[y].pb(x);
}
if(n==2){
puts(e[1].size()?"YES":"NO\n1 2");
continue;
}
rep(i,1,n){
if(g[i].size()!=1)rt=i;
else if(e[i].empty()){
printf("NO\n%d %d\n",i,i==1?n:1);
goto next;
}
}
tot=0;
dfs(rt,0);
mx=pt=0;
rep(i,1,n)if(g[i].size()==1){
ps=0;
for(int j:e[i]){
int x=lca(i,j);
if(!ps||d[x]<d[ps])ps=x;
}
if(!mx||d[ps]>d[mx])mx=ps,pt=i;
}
rep(i,1,n)if(g[i].size()==1){
for(int j:e[i]){
int x=lca(i,j);
if(in(x,mx)&&(in(mx,i)||in(mx,j)))goto nxt;
}
printf("NO\n%d %d\n",i,pt);
goto next;
nxt:;
}
puts("YES");
next:;
}
return 0;
}