题目大意
题目大意
给定一棵包含 个节点的树,树上分布着 种钥匙(编号 到 )和 种锁(编号 到 )。每种钥匙和每种锁在树上恰好出现一次。
节点信息由数组 给出:
-
若 ,表示该节点拥有编号为 的钥匙。
-
若 ,表示该节点拥有编号为 的锁。
移动与收集规则
-
出发点:必须选择一个拥有钥匙的节点作为起点。
-
钥匙获取:到达(或经过)拥有钥匙的节点时,自动获得该钥匙。钥匙永久保留。
-
通行限制:只有当手中拥有编号为 的钥匙时,才能进入拥有编号为 的锁的节点。
目标
对于每一种钥匙 (),判断:如果从拥有钥匙 的节点出发,是否能够遍历树的足够多部分,从而收集到全部 种钥匙。
输入格式
-
第一行包含整数 ,表示测试数据组数。
-
每组数据包含:
- 整数 。
- 个整数 ,表示节点上的物品。
- 行,每行两个整数 ,表示树边。
输出格式
对于每组数据,输出一个长度为 的 01字符串。
-
字符串第 个字符对应编号为 的钥匙作为起点的结果。
-
若能收集所有钥匙输出
1,否则输出0。
样例
1 | Input |
样例解释(以第二组数据为例)
- 配置:。
- 节点 1:锁 3 (-3)
- 节点 2:钥匙 2 (2)
- 节点 3:钥匙 1 (1)
- 节点 4:锁 1 (-1)
- 节点 5:钥匙 3 (3)
- 节点 6:锁 2 (-2)
- 边连接结构:4-2-1-3-6,且 2-5 相连。即 1 是中心,连接 2 和 3;2 连接 4 和 5;3 连接 6。

-
判定过程:
- 从钥匙 1 出发(位于节点 3):
- 初始拥有:{钥匙 1}。
- 相邻节点:节点 1(锁 3,无钥匙 3,不可入),节点 6(锁 2,无钥匙 2,不可入)。
- 结果:困在节点 3,无法收集所有。对应字符串第 1 位为 0。
- 从钥匙 2 出发(位于节点 2):
- 初始拥有:{钥匙 2}。
- 相邻节点:节点 1(锁 3,不可入),节点 4(锁 1,不可入),节点 5(钥匙 3,可入)。
- 移动到节点 5,获得钥匙 3。现有:{钥匙 2, 钥匙 3}。
- 回到节点 2,尝试进入节点 1(锁 3)。因有钥匙 3,可入。
- 到达节点 1 后,可前往节点 3(钥匙 1,可入)。获得钥匙 1。
- 现有:{钥匙 1, 2, 3}。收集完毕。对应字符串第 2 位为 1。
- 从钥匙 3 出发(位于节点 5):
- 初始拥有:{钥匙 3}。
- 前往节点 2(钥匙 2,可入)。获得钥匙 2。
- 现有:{钥匙 2, 3}。后续过程同上,可收集完毕。对应字符串第 3 位为 1。
-
最终输出:
011。
思路讲解
首先,我们想到暴力,但是跑一次只需要 ,也要 才能跑完。
于是面对这种复杂的问题,我们想到,需要分而治之。
我们先想,如果只有一个钥匙和一个门,那么合法的点在哪里那?

不难发现,肯定是在这边半颗树,也就是有钥匙的子树上的点才是合法的。
那么这个结论是非常显然的,那么现在问题在于我们有非常多的门,其实我们运用一下这个结论就会发现,我们只要不断往门上含有钥匙的子树上走,就行了,最后我们会得到一个联通块,检查一下这个联通块的联通性即可。
AC代码
https://vjudge.net/solution/64200130
1 | // by Gospel_rock |
心路历程(WA,TLE,MLE……)
那么一开始,我们选择的是纯 dfs 解决该问题,但是 WA 了。
1 | vll valid; |
主要原因是使用 dfs 时,没有一个全局的信息来参考。
比如下面这组 hack 数据,我的程序会错误地把 2,3 加入 valid 数组中,这个显然是有问题的。
这个其实是因为我们先走了节点 3 这条支路,又走了节点4。

于是我们继续在想,用树上差分固然可以解决此问题,但是总觉得有点杀鸡用牛刀了。使用纯 dfs 方案用办法解决此问题吗?
我们发现这个问题在于我们走完一条路了以后,我们需要知道这条路是封闭的,还是会继续往下走的?其实只要是会继续往下走的,其他路也不需要走了。
知道了这个以后,我们发现,其实不用改动很多,仅仅只需要加一个返回值,子树返回值为 1 时,我们直接开摆即可。
1 | // return 1 表示的是可以往下走 |
然后就 AC 了。