题意:一颗由 n 个顶点,n - 1 条边组成的树,判断能否一条路走完给出的 k 个顶点。
思路:这 k 个点会有两种情况,一种情况是从一个点只有一条路到底,还有一种情况从一个点分成两条路走下去。我们先从 k 个点中找出两个不在同一条路最深的点 t1 和点 t2。如果只能找到一个点,说明是第一种情况,直接输出 YES。第二种情况:我们求出点 t1 和点 t2 的 lca 点m,那么这 k 个点必须在点 t1 和点 m 之间或者是点 t2 和点 m 之间。如果在点 t1 和点 m 之间, 那么这个点和点 t1 的 lca 就是这个点本身,和点 t2 的 lca 就是点 m,同理可以判断这个点是否在 t2 和 m 之间。如果所有点都满足情况,那么说明这 k 个点在一条路上。
代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10, P = 1e9 + 7, mod = 998244353;
vector<int>a[N];
int dep[N], fa[N][30];
int p[N];
int sz;
void dfs(int x, int f){
dep[x] = dep[f] + 1;
fa[x][0] = f;
for(int i = 1; (1 << i) <= dep[x]; i++)
fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(int i = 0; i < a[x].size(); i++)
if(a[x][i] != f) dfs(a[x][i], x);
}
int lca(int x, int y){
if(dep[x] < dep[y]) swap(x, y);
for(int i = sz; i >= 0; i--)
if(dep[fa[x][i]] >= dep[y])
x = fa[x][i];
if(x == y) return x;
for(int i = sz; i >= 0; i--)
if(fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
void solve(){
int n, q;
cin >> n;
sz = 0;
while((1 << sz) < n) sz++;
for(int i = 1; i < n; i++){
int u, v;
cin >> u >> v;
a[u].pb(v), a[v].pb(u);
}
dfs(1, 0);
cin >> q;
while(q--){
int k; cin >> k;
for(int i = 1; i <= k; i++) cin >> p[i];
int t1 = 0, t2 = 0;
for(int i = 1; i <= k; i++)
if(dep[p[i]] > dep[t1]) t1 = p[i];
for(int i = 1; i <= k; i++)
if(lca(p[i], t1) != p[i] && dep[p[i]] > dep[t2])
t2 = p[i];
if(!t2){
cout << "YES" << endl;
continue;
}
int m = lca(t1, t2);
bool ok = 1;
for(int i = 1; i <= k; i++){
int m1 = lca(p[i], t1);
int m2 = lca(p[i], t2);
if(!((m1 == p[i] && m2 == m) || (m2 == p[i] && m1 == m)))
ok = 0;
}
if(ok) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
//int t;
//cin >>t;
//while(t--){
solve();
//}
return 0;
}