CodeForces-1702G Passable Paths
Passable Paths
LCA
在树上找到形容一条链,只用找到链的两个端点即可,因此这题的初始想法就是找端点
第一个端点:深度最深的地方
第二个端点:离第一个端点最远的那个点
找到两个端点之后,就判断一下其他点是否在这个链上:最快的方法就是判断两个端点到这个点 p
的距离之和,是不是和链长相等
以上倍增 LCA 实现一下就好了,距离的话直接找到根的距离,也就是深度差,容斥一下算距离就好了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
vector<int>gra[maxn];
int dep[maxn], fa[maxn][25], pa[maxn];
void dfs(int now, int pre, int d)
{
dep[now] = d;
fa[now][0] = pre;
for(auto nex : gra[now])
{
if(nex == pre) continue;
dfs(nex, now, d + 1);
}
}
void init(int n, int rt = 1)
{
dfs(rt, rt, 0);
for(int i=1; i<=20; i++)
for(int j=1; j<=n; j++)
fa[j][i] = fa[fa[j][i-1]][i-1];
}
int LCA(int a, int b)
{
if(dep[a] < dep[b]) swap(a, b);
int dif = dep[a] - dep[b];
for(int i=20; i>=0; i--)
{
if(dif >= (1 << i))
{
dif -= 1 << i;
a = fa[a][i];
}
}
if(a == b) return a;
for(int i=20; i>=0; i--)
{
if(fa[a][i] != fa[b][i])
{
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];
}
int dis(int a, int b)
{
return dep[a] + dep[b] - 2 * dep[LCA(a, b)];
}
int main()
{
int n;
scanf("%d", &n);
for(int i=1; i<n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
gra[x].push_back(y);
gra[y].push_back(x);
}
init(n);
int m;
scanf("%d", &m);
while(m--)
{
int k;
scanf("%d", &k);
int d = -1, a = 0, b = 0, f = 1;
for(int i=0; i<k; i++)
{
scanf("%d", &pa[i]);
if(dep[pa[i]] > d) {a = pa[i]; d = dep[pa[i]];}
}
d = -1;
for(int i=0; i<k; i++)
{
int x = dis(a, pa[i]);
if(d < x)
{
b = pa[i];
d = x;
}
}
for(int i=0; i<k && f; i++)
{
if(dis(a, pa[i]) + dis(b, pa[i]) != d)
f = 0;
}
printf("%s\n", f ? "yes" : "no");
}
return 0;
}