P10076 [GDKOI2024 普及组] 捉迷藏 题解
学校模拟赛 vp 了普及组第二天,喜提
题意简述
给定一棵树,再给出树上两点的位置。两点交替移动,第一个点每次可以移动
当一个点移动到另一个点的位置上时,该点获胜。
问哪个点会获胜,或告知平局。
思路
我们分情况讨论。
下文
开始时第一个点就能移动到第二个点的位置上
那么就说明
这种情况肯定是第一个点获胜。
da = db
显然,当第一个点移动到再移动一次就能满足
而且,第一个点移动之后,就轮到第二个点移动了,因为
所以两个点永远都不会让
da > db
那么就肯定存在满足第一个点移动后,
此时第二个点无法移动到第一个点,而且若不移动到第一个点的移动范围外,再轮到第一个点移动时,就会让第一个点移动到自己的位置上。
所以第二个点就只能躲避第一个点。
但是树上不存在环,躲到最后肯定会无路可逃,所以这种情况肯定是第一个点赢。
db > da
由上个情况同理可证:这种情况肯定是第二个点赢。
Code
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1000005;
struct Edge{
int to, nxt;
}g[N << 1];
int h[N], idx;
int dep[N], f[N][18];
void add(int a,int b)
{
g[++idx].to = b, g[idx].nxt = h[a], h[a] = idx;
}
void dfs(int u,int fa)
{
for (int i = h[u]; i; i = g[i].nxt)
{
int j = g[i].to;
if (j == fa)
continue;
dep[j] = dep[u] + 1;
f[j][0] = u;
for (int k=1;k<18;k++)
f[j][k] = f[f[j][k - 1]][k - 1];
dfs(j, u);
}
}
int lca(int a,int b)
{
if (dep[a] < dep[b])
swap(a, b);
for (int i=17;i>=0;i--)
if (dep[f[a][i]] >= dep[b])
a = f[a][i];
if (a == b)
return a;
for (int i=17;i>=0;i--)
if (f[a][i] != f[b][i])
{
a = f[a][i];
b = f[b][i];
}
return f[a][0];
}
inline int dist(int a,int b)
{
return dep[a] + dep[b] - 2 * dep[lca(a, b)];
}
int main()
{
int T;
scanf("%*d%d", &T);
while (T--)
{
int n, q;
scanf("%d%d", &n, &q);
for (int i=1;i<=n;i++)
h[i] = 0;
idx = 0;
for (int i=1;i<n;i++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
dfs(1, 1);
while (q--)
{
int a, b, da, db;
scanf("%d%d%d%d", &a, &b, &da, &db);
if (dist(a, b) <= da)
puts("Zayin");
else if (da == db)
puts("Draw");
else if (da > db)
puts("Zayin");
else
puts("Ziyin");
}
}
return 0;
}