题目链接
题意:给定n点,n-1条边的树,有m个询问,每次询问会给你一个数组,问你树中是否存在着一条从1到某个点的路径包含数组中的所有点,或者数组中的这些点距离这条路径的路径上的点的距离小于等于1.
思路:很明显如果存在这条路径的话,那么给定的这些数组的点和路径上的点要么重合,要么就是它的兄弟节点。比如第一个样例,我们选最深的点10,然后就选择树上1到10的这条路径,很显然8和9是兄弟节点所以8也是符合条件的。我们用优先队列来优先选择深度最深的点,然后一步步向前推(这个地方一开始想用pre一步步推,结果tle test100.。。。),所以这个向前推的过程可以优化一下,用lca就行了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+1;
int deep[maxn],pre[maxn],parent[maxn][22];
vector<int>g[maxn];
void dfs(int x,int fa)
{
deep[x]=deep[fa]+1;
pre[x]=fa;
parent[x][0]=fa;
for(int to:g[x])
{
if(to==fa) continue;
dfs(to,x);
}
}
struct cmp{
bool operator () ( int a , int b ){
return deep[a]<deep[b];
}
};
void init(int n)
{
for(int k=1;k<=21;++k)
for(int i=1;i<=n;++i)
parent[i][k]=parent[parent[i][k-1]][k-1];
}
int main()
{
priority_queue < int , vector<int> , cmp > q;
int n,m,u,v,k,x;
scanf("%d %d",&n,&m);
for(int i=1;i<n;++i)
{
scanf("%d %d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
deep[1]=0;
dfs(1,0);
init(n);
while(m--)
{
while(!q.empty())q.pop();
scanf("%d",&k);
for(int i=1;i<=k;++i)
scanf("%d",&x),q.push(x);
int t=q.top();q.pop();
while(t!=0&&!q.empty())
{
for(int i=21;i>=0;--i)
if(deep[parent[t][i]]>=deep[q.top()]) t=parent[t][i];
if(t==q.top()||pre[t]==pre[q.top()]) q.pop();
else break;
}
if(!q.empty()) puts("NO");
else puts("YES");
}
}