题目大意
链接
给定一棵树,每次给定一些点,求最少要阻断多少点使得这些点之间两两之间无法通行。其中不能阻断给定点,无解输出-1.
分析
虚树上贪心/DP即可。
虚树
由于在树上有可能有很多点根本就不需要用,所以我们要把这些点提出来单独组成一棵树,减少了大量运算。
一般来说我们要提出的点是所有的点和这些点之间两两LCA的点,然而K个点LCA的点仅仅有K-1个,也就是按照DFS序排序之后相邻的点之间LCA即可。
我挑了一种比较好理解和易于自己实现的写法。
首先获得原树,然后遍历之后得到DFS序,并且拥有一个办法求LCA,求出DFS序,求出每个点所在子树的时间戳范围。
对于每一组数据我们有如下操作:
- 获取结点,按照DFS序排序,相邻结点之间获取它们的LCA,再把所有结点按照DFS序排序,去重。
- 弄一个栈,先加入DFS序最小的,它也就是整个虚树的根。
- 依次加入结点,如果当前结点不在栈顶结点的子树内,就弹出栈顶结点。然后连接栈顶元素和当前元素,把当前元素入栈。
好了虚树就构建好了。
说几个注意的点:
- 建立虚树的时候是个有根树,可以不向根的方向连边,因此遍历的时候不用判断父亲结点。
- 边和其它数据的还原最好利用我们得到的结点逐个还原,因为原树太大了。
- 注意排序的时候要加cmp函数,但是unique的时候不要加。
代码
#include<cmath>
#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=3e5+105,inf=1e6,oo=18;
int n,m;
int dfn,l[maxn],r[maxn],dep[maxn],fa[maxn]