題目鏈接
題意:給定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");
}
}