【专题】线性判定排列逆序数的奇偶性
排列逆序数的奇偶性是一个十分常见的属性。不同于直接求逆序数,由于排列的性质,这玩意是可以 \(\mathcal O(n)\) 直接求解的。为了完成这一点,引入如下基本结论:
- 排列两元素对换,逆序数奇偶性改变。
- 排列的逆序数同余 \(n - \#\)环。
第一点,在大多数线性代数教材中都有所提及。
第二点中的 “环” 的构造方式:按下标 \(i\) 向 \(p_i\) 连边,或者说,两排列之间连边,必形成若干环(这是因为每个数都出现一次,对应的入/出度数为 \(1\))。
定义排列的复合 \((f \circ g)_x\) 将 \(x\) 变为 \(f_{g_{x}}\),下证
\[\operatorname{parity}(f \circ g) = \operatorname{parity}(f) + \operatorname{parity}(g) \]\(f\) 经过 \(\operatorname{swap}(i, j)\) 后,变为 \(f'\),其奇偶性变为 \(\operatorname{parity}(f) + 1\)。可表示为 \(f \circ g = f'\),其中排列 \(f, g\) 仅有 \((i, j)\) 两点顺序不同。这过程可以归纳,得到上述结论不难。
为啥是这个数[1]?考虑 \(\operatorname{swap}(i, j)\) 在从 \(i\) 到 \(p_i\) 的图上是什么含义?注意到如果交换环上两点,将会产生一个自环,环的大小会减一。用上面的话来说,对于环 \(c_i \in \{C\}_k\),\(\operatorname{swap}(i, j)\) 实为 \(f \circ c_1 \circ c_2 \circ \cdots \circ c_k\),而在这个环上的奇偶变化为 \(k - 1\),将所有环的贡献相加就得到:
这个结论十分有用。
有点记不清了但至少 ABC 出过三四次了,每次看蒋老师代码都觉得“哦,这tm我学过啊”真是十分常见的结论。下附代码:
bool parity = n & 1;
for (int i : p) if (~i) {
for (int j = i; ~j; ) {
std::tie(j, p[j]) = std::tuple{p[j], -1};
}
parity ^= 1;
}
参考 CodeForces Blog: https://codeforces.com/blog/entry/97849 ↩︎