题目链接
题目大意:
给你一棵树,根为节点1,进行m次查询,每次查询k个节点。判断是否存在一条由根到节点u的路径,使k个节点在这条路径上或距离这条路径距离不大于1.
思路:
每次选取距离根深度最深的点fv,因为深度最深的点总是最难满足条件的点,剩下的k-1个点要么在路径上(是点fv的祖先),要么距离路径的距离为1(点集中点的第一个父结点是点fv的祖先),因为fv祖先的父结点还是 fv的祖先,我们让所有点都找到它的父结点,便于判断条件。这样,就把问题转换成了判断剩下 k-1个点的父结点是不是fv的祖先。
之后,想要判断一个点是不是另一个点的祖先,就用到了一种非常巧妙的方法(类似线段树),将每一个点与它的子树转换成一个区间,当节点v的区间可覆盖节点u的区间,v即为u的祖先!详情请看代码。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
#define endl "\n"
#define pii pair<int,int>
#define pb push_back
#define debug(x) cout << "visit:" << x << endl
#define inf 2147483647
using namespace std;
const int N = 2e5+5;
vector<int> g[N];
int p[N], d[N], tin[N], tout[N];
int v[N];
int T = 0;
void dfs(int v,int par,int dep)
{
p[v] = par;
d[v] = dep;
tin[v] = T++;
for(int i=0;i<g[v].size();++i)
{
if(g[v][i]==par)
{
continue;
}
else
{
dfs(g[v][i],v,dep+1);
}
}
tout[v] = T++;
}
bool isAnc(int v,int u)
{
return tin[v]<=tin[u]&&tout[u]<=tout[v];
}
int main()
{
fast;
int n, m;
cin >> n >> m;
for(int i=1;i<=n-1;++i)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,0,0);
for(int i=0;i<m;++i)
{
int k;
cin >> k;
for(int j=0;j<k;++j)
{
cin >> v[j];
}
int u=v[0];
for(int j=1;j<k;++j)
{
if(d[u]<d[v[j]])
{
u = v[j];
}
}
for(int j=0;j<k;++j)
{
if(v[j]==u)
{
continue;
}
if(p[v[j]]!=0)
{
v[j] = p[v[j]];
}
}
bool flag = 1;
for(int j=0;j<k;++j)
{
if(!isAnc(v[j],u))
{
flag = 0;
break;
}
}
if(flag)
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
return 0;
}
/***********************************************************/
/***********************************************************/
/***** A CCCCCC PPPPP L ZZZZZZ **/
/**** A A C P P L Z ***/
/*** A A C PPPPPP L Z ****/
/** AAAAAAA C P L Z *****/
/* A A CCCCCC P LLLLL ZZZZZZ ****/
/***********************************************************/
/***********************************************************/