做了这场的最后两题明显感觉比上一场的div3难啊。
题意:给一棵树,每次查询包含若干个点,问是否从根存在一条路径,是每个点或在这条路径上,或离路径上的点最近距离为1。
通过画图比划不难发现,一个点如果满足如上条件,则它的父亲一定在路径上,因为树结构的特殊性,如果某结点的儿子在路径中,则此节点一定也在路径中。
对于同一深度的所有节点,最多只有一个结点在路径上。所以,我们先将k个结点按照深度排序,进行遍历,求出每两个点x,y的最近公共祖先,如果它们的LCA是x,说明x在路径上,且可以延伸到y的父亲。如果它们的LCA是fa[x],说明经过了fa[x],x此时已满足条件,并将路径延伸到fa[y]。至于y和后面会不会有冲突,需要在下一次求的时候在判断。
总复杂度(O(KlogK+KlogN));
使链向星存树。
PS:如果不知道LCA是什么,可去luogu上找模板题学。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include<vector>
using namespace std;
struct zzz {
int t, nex;
} e[300010 << 1];
int head[200010], tot;
void add(int x, int y) {
e[++tot].t = y;
e[tot].nex = head[x];
head[x] = tot;
}
int times=0;
int dfn[200010];
int depth[500001], fa[500001][26], lg[500001];
void dfs(int now, int fath) {
fa[now][0] = fath;
depth[now] = depth[fath] + 1;
for(int i = 1; i <= lg[depth[now]]; ++i)
fa[now][i] = fa[fa[now][i-1]][i-1];
for(int i = head[now]; i; i = e[i].nex)
if(e[i].t != fath) dfs(e[i].t, now);
}
int LCA(int x, int y)
{
if(depth[x] < depth[y]) swap(x, y);
while(depth[x] > depth[y])
x = fa[x][lg[depth[x]-depth[y]] - 1];
if(x == y) return x;
for(int k = lg[depth[x]] - 1; k >= 0; --k)
if(fa[x][k] != fa[y][k])
x = fa[x][k], y = fa[y][k];
return fa[x][0];
}
int k;
int save[200010];
int fl=1;
int x, y;
struct node {
int d;
int index;
} tree[200010];
bool cmp(node x,node y) {
return x.d<y.d;
}
int n, m;
int main() {
scanf("%d%d", &n, &m );
for(int i = 1; i <= n-1; ++i) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
for(int i = 1; i <= n; ++i)
lg[i] = lg[i-1] + (1 << lg[i-1] == i);
dfs(1, 0);
for(int j = 1; j <= m; ++j)
{
fl=1;
scanf("%d",&k);
for(int i=1; i<=k; i++)
{
scanf("%d",&save[i]);
tree[i].index=save[i];
tree[i].d=depth[tree[i].index];
}
sort(tree+1,tree+k+1,cmp);
for(int i=2; i<=k; i++)
{
x=tree[i-1].index;
y=tree[i].index;
if (x==1) {
continue;
}
if (LCA(y,x)!=x&&LCA(y,x)!=fa[x][0])
{
fl=0;
}
}
if (fl) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
后来发现别人有O(K)的做法,是用dfs序做的。
不得不说自己还是太菜了。
有问题可以在下面评论。如果对你有帮助,点个赞再走幺。