Codeforces Round #629 (Div. 3) E. Tree Queries(LCA+思维)

题目链接
在这里插入图片描述
在这里插入图片描述
题意:给定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");
	 }
}
发布了328 篇原创文章 · 获赞 1 · 访问量 9120

猜你喜欢

转载自blog.csdn.net/qq_42479630/article/details/105136101