[日常训练] 树上的游戏
程序员文章站
2022-05-20 16:45:53
...
【问题描述】
jmy在大家的帮助下终于成功**了咒语。但是他很不服气,于是又准备和lkf玩游戏,想要赢回来。
由于之前一直在找最小生成树,jmy想到了一个树上的游戏:给定一棵树,lkf先在这棵树上加一条边(可能出现重边),jmy需要删掉其中的一条边,如果剩下的还是一棵树则jmy赢,否则lkf赢。
问题是,现在jmy不知道lkf会加哪一条边,也不知道自己应该删掉哪一条边。于是现在jmy臆测出了许多种可能,作为jmy的好朋友,你要告诉他:如果lkf在编号为x的点和编号为y的点之间加一条边,jmy删掉第z条边(边按照输入的顺序编号,不包括新加的边)是否能赢。
【输入格式】
第一行一个正整数n,表示节点个数。
接下来n-1行,每行两个正整数x,y,表示原来树上存在一条连接编号为x的节点和编号为y的节点的边。
第n+1行一个正整数Q,表示询问次数。
接下来Q行,每行三个正整数x,y,z,表示一个询问(含义如题所示)。
【输出格式】
输出共Q行。
对于每一个询问,如果lkf会赢就输出YES,否则输出NO。
【输入输出样例】
tree.in
5
1 2
2 3
2 4
4 5
3
2 5 3
2 3 1
1 5 2
tree.out
NO
YES
YES
【数据规模】
对于20%的数据保证n,Q≤1000。
对于另外20%的数据保证n,Q≤10000且树为随机生成。
对于70%的数据保证n,Q≤200000。
对于100%的数据保证n≤200000,Q≤2000000。
【分析】 DFS序
- 删除一条边后原图显然变成了两棵树,则我们只要考虑在两棵树上分别选一点相连就是一种合法方案
- 则问题被转化为删除边
u↔v ,则点若添加边的两点x,y 不同时在u 或v 所对应的树中,新的图仍为一棵树。 - 我们可以用
DFS 序(DFS 遍历树的顺序)来判断,先预处理出点x 的DFS 序为num[x] ,以x 为根的子树大小为sze[x] ,则若点x 在点u 的子树中,必然要满足num[u]≤num[x]≤num[u]+sze[u]−1 。也就是说,若x,y 都为u 的子树或都不为u 的子树,则新的图不为树 - 但这样还有一个问题,这里的
u 要满足遍历的深度dep[u]<dep[v] ,因为v(dep[v]>dep[u]) 的子树同时也包括u 的子树,如果按照v 来计算,显然是有问题的
【代码】
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 2e5 + 5, M = N << 1;
int lst[N], nxt[M], to[M], fr[M];
int num[N], sze[N], dep[N];
int T = 1, E, n, Q;
inline void addEdge(const int &x, const int &y)
{
nxt[++T] = lst[x]; lst[x] = T; to[T] = y; fr[T] = x;
nxt[++T] = lst[y]; lst[y] = T; to[T] = x; fr[T] = y;
}
inline void Dfs(const int &x, const int &fa)
{
num[x] = ++E; sze[x] = 1;
dep[x] = dep[fa] + 1;
for (int i = lst[x]; i; i = nxt[i])
{
int y = to[i];
if (y == fa) continue;
Dfs(y, x); sze[x] += sze[y];
}
}
inline bool Query(const int &x, const int &y)
{
return (num[y] >= num[x] && num[y] <= num[x] + sze[x] - 1);
}
inline void Swap(int &x, int &y) {int t = x; x = y; y = t;}
inline int get()
{
char ch; int res;
while ((ch = getchar()) < '0' || ch > '9');
res = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9')
res = (res << 3) + (res << 1) + ch - '0';
return res;
}
int main()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
n = get();
for (int i = 1; i < n; ++i) addEdge(get(), get());
Dfs(1, 0); Q = get(); int x, y, z, u, v;
while (Q--)
{
x = get(); y = get(); z = get();
u = fr[z << 1]; v = to[z << 1];
if (dep[u] < dep[v]) Swap(u, v);
puts((Query(u, x) == Query(u, y)) ? "YES":"NO");
}
fclose(stdin); fclose(stdout);
return 0;
}
上一篇: 问题 D: 【宽搜入门】魔板
下一篇: 图的深度优先遍历寻路算法