题意
给出一棵树,问是否存在1到某个结点的一条路径,使得k个结点到这条路径的距离<=1.
思路
在k个结点中,找深度最大的这个结点mx,然后与每个结点求ui = LCA(mx, ki),判断dis(ui, ki) <= 1即可。
时间复杂度m * k * logn
AC代码
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define IOS ios::sync_with_stdio(false)
const int maxn = 4e5 + 100;
struct Edge{
int to, w, next;
}edge[maxn];
int dis[maxn], fa[21][maxn], head[maxn], path[maxn], node[maxn], deep[maxn], cnt;
bool vis[maxn];
void init(){
memset(head, -1, sizeof(head));
memset(fa, 0, sizeof(fa));
memset(deep, 0, sizeof(deep));
memset(vis, false, sizeof(vis));
memset(dis, 0, sizeof(dis));
cnt = 0;
}
void add(int u, int v, int w){
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void dfs(int u, int fno){
for (int i = 1; i <= 20; ++i){
fa[i][u] = fa[i - 1][fa[i - 1][u]];
}
for (int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if (v == fno) continue;
dis[v] = dis[u] + edge[i].w;
fa[0][v] = u;
deep[v] = deep[u] + 1;
dfs(v, u);
}
}
int LCA(int x, int y){
if (deep[x] < deep[y]){
swap(x, y);
}
for (int i = 20; i >= 0; --i){
if (deep[x] -deep[y] >> i){
x = fa[i][x];
}
}
if (x == y) return x;
for (int i = 20; i >= 0; --i){
if (fa[i][x] != fa[i][y]){
x = fa[i][x];
y = fa[i][y];
}
}
return fa[0][x];
}
void solve(){
int n, m, u, v;
scanf("%d%d", &n, &m);
init();
for (int i = 1; i < n; ++i){
scanf("%d%d", &u, &v);
add(u, v, 1);
add(v, u, 1);
}
dfs(1, -1);
int k;
while (m--){
scanf("%d", &k);
int mx = 0, base = 0;
for (int i = 0; i < k; ++i){
scanf("%d", &node[i]);
if (mx < deep[node[i]]){
mx = deep[node[i]];
base = node[i];
}
}
bool flag = true;
for (int i = 0; i < k; ++i){
int lca = LCA(base, node[i]);
if (dis[lca] + dis[node[i]] - dis[lca] * 2 > 1){
flag = false;
break;
}
}
if (flag) puts("YES");
else puts("NO");
}
}
int main(){
solve();
return 0;
}