题解:CF2002D2 DFS Checker (Hard Version)
masonxiong · · 题解
前面转自我的 D1 题解。
题目分析
题解记号说明:
这场做起来最顺手的题目就是 D 了。
每次
我们知道,若
注意,这是充分必要条件。下面我们对这个性质进行证明。
充分性证明
即:若
我们按照
它是一个非叶子节点
若
它是一个叶子节点
若
充分性证明完毕。
必要性证明
即:若
我们任取一个编号为
这样,我们知道每一个子树至多被访问一次,且肯定在一个合法 DFS 序中的一个连续的子序列中,并且第一个访问到的节点肯定是这个子树的根节点。这足以说明
题目做法
我们有了这条性质,那我们就可以对于每一个
每次操作时,更新
那么现在的重点就在于:给定
转 LCA
D1 题解中的暴力跳解法本质上就是判祖先转成 LCA。根据如下性质:
我们可以采取任何的技术来高效求解
size 搭配 dfn
利用
证明:根据前面的证明,我们知道每一个子树肯定在一个合法 DFS 序中的一个连续的子序列中,并且第一个访问到的节点肯定是这个子树的根节点。
那么根据定义,
u 号节点为根的子树所对应的连续子序列的左端点就是dfn_u ,长度是size_u ,则这个连续子序列就是[dfn_u,dfn_u+size_u) 。祖先节点为根的子树肯定包含后辈节点为根的子树,所以它们对应的连续子序列应当也满足这个包含关系。
此解法可以
代码
以
#include <bits/stdc++.h>
using namespace std;
const int MaxN = 300000 + 5;
int T, N, Q, dfsTime, validCount;
int dfsOrder[MaxN], dfn[MaxN], parent[MaxN], treeSize[MaxN];
bitset<MaxN> validPosition;
vector<vector<int>> tree;
bool isAncestor(int ancestor, int descendant) {
return dfn[ancestor] <= dfn[descendant] && dfn[ancestor] + treeSize[ancestor] >= dfn[descendant] + treeSize[descendant];
}
bool initialize() {
function<int(int)> dfs = [&](int current) {
treeSize[current] = 1;
dfn[current] = ++dfsTime;
for (int i : tree[current])
treeSize[current] += dfs(i);
return treeSize[current];
};
cin >> N >> Q;
validCount = 0;
validPosition.reset();
tree.assign(N + 1, vector<int>());
for (int i = 2; i <= N; i++) {
cin >> parent[i];
tree[parent[i]].push_back(i);
}
for (int i = 1; i <= N; cin >> dfsOrder[i++]);
dfs(1);
for (int i = 1; i < N; i++)
validCount += validPosition[i] = isAncestor(parent[dfsOrder[i + 1]], dfsOrder[i]);
return true;
}
bool updateAnswer(int x, int y) {
function<void(int)> updateValidPosition = [&](int position) {
if (position != 0 && position != N && isAncestor(parent[dfsOrder[position + 1]], dfsOrder[position]) ^ validPosition[position]) {
validPosition[position] = isAncestor(parent[dfsOrder[position + 1]], dfsOrder[position]);
validCount += validPosition[position] ? 1 : -1;
}
};
swap(dfsOrder[x], dfsOrder[y]);
updateValidPosition(x); updateValidPosition(x - 1);
updateValidPosition(y); updateValidPosition(y - 1);
return validCount == N - 1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
for (cin >> T; T-- && initialize(); )
for (int x, y; Q--; cout << (updateAnswer(x, y) ? "Yes\n" : "No\n"))
cin >> x >> y;
return 0;
}