【代码超详解】DFS 序及其模板题之一 CF1006E Military Problem

一、DFS 序

DFS 序是指:每个节点进出栈的时间序列。

例如:对上面这棵树,其 DFS 序列是:

它是这样被列写出来的:从顶点 A 出发,A 有子节点 B 和 C ,先访问 B ;B 有子节点 E ,访问 E ;E无子节点,退出(序列上也要再添加一个 E ),访问 F ;F 有子节点 K ,访问 K ;K 无子节点,退出;……
就完成了 DFS 序的列写。

但是有时候我们并不需要将 DFS 过程中退出某个节点的次序也记录下来,只需记录访问各节点的先后顺序。我们来看 CodeForces 1006E
有一个多叉树,根节点编号为 1 ,代表 Berland 军队中的最高指挥官。其余节点依次编号2、3、4、……,分别代表2、3、4、……号军官。每个节点的父节点是其直接上级,每个节点的子节点代表其下属。

输入第一行为 n 和 q ,代表节点数和询问次数。接下来一行包含 n-1 个数 p2、p3、……、pn ,意思是:pi 是 i 的直接上级。
接下来 q 行,每行 2 个数 u 、p ,分别代表下命令的军官和第 p 个收到命令的军官。发送命令的先后顺序是 DFS 决定的。例如:
If officer 1 spreads a command, officers receive it in the following order: [1,2,3,5,6,8,7,9,4].
If officer 3 spreads a command, officers receive it in the following order: [3,5,6,8,7,9].
If officer 7 spreads a command, officers receive it in the following order: [7,9].
If officer 9 spreads a command, officers receive it in the following order: [9].
对每个询问:
u p
输出第 p 个收到命令的军官的编号。其中 2 ≤ n, q ≤ 200000 。

解 显然这题是模板题,各军官发送命令的先后顺序在图上完全符合 DFS 序。因此直接套用前面提供的 DFS 模板 即可。
但是这棵多叉树可以有 200000 个节点,询问也可以有 200000 次。如果我们对每个询问都在这么大一张图进行一次 DFS ,一定会超时超到你哭天喊地。所以我们尝试借鉴莫队的思想,先将询问一次录入,然后排序,如果是同一个指挥官发送命令,就只需要一次 DFS 。
代码:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<bitset>
#include<map>
#include<vector>
#pragma warning(disable:4996)
using namespace std;
struct Edge {
    unsigned NextStoPos = 0, to = 0; };
Edge e[2000000]; unsigned head[200001], u, v; bitset<200001> b; vector<unsigned> o; vector<pair<unsigned, unsigned>> Q, R;
map<pair<unsigned, unsigned>, unsigned> A;
const unsigned Init_RevIdx = 0; pair<unsigned, unsigned> p;
inline void AddEdge(unsigned* RevIdx, Edge* Edge, const unsigned& vtx_u, const unsigned& vtx_v, unsigned& Idx_of_Sto) {
   
	Edge[Idx_of_Sto].to = vtx_v; Edge[Idx_of_Sto].NextStoPos = RevIdx[vtx_u];
	RevIdx[vtx_u] = Idx_of_Sto;
}
template<size_t n> inline void DFS_PrtSeq(const unsigned& vertex, vector<unsigned>& OutSeq, const unsigned
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值