P8449 [LSOT-1] 逆序对 题解
思路
数学,树状数组
数学
首先,观察题目,对每个操作进行分析。
发现操作 3 与操作 4 容易用树状数组来维护(关于为什么操作 3 和 操作 4 不会影响之后的 1 和 2,可以先向后看)。
而操作 2 可以转化为区间 \([l, r]\) 与区间 \([r + 1, k]\) 互换。所以仅需思考如何解决操作 1 。
(如果你不知道树状数组如何解决逆序对问题的话,可以先做做这个)
题目仅询问逆序对的奇偶性,并不关心序列具体怎样。考虑交换两个区间对逆序对的奇偶性有什么影响。
有真命题:交换两序列,逆序对的奇偶性变化仅与交换的两个序列的长度有关。
证明: 如图,交换序列 \(A\) 与序列 \(C\) 。
首先考虑这对 \(B\) 与 \(E\) 的影响。
因为 \(A, C\) 仍然在 \(B\) 之后,所以产生逆序对的数量不变,对于 \(E\) 来说,同理。
再考虑对于 \(D\) 来说逆序对奇偶性的变化。
设 \(A,B,D\) 的长度分别为 \(la,lb,ld\) 。
对与 \(A\) 中的 \(a_i\) ,\(D\) 中有 \(b_i\) 个数大于它, \(ld - b_i\) 个数小于它。
对于 \(C\) 中的 \(c_i\) ,\(D\) 中有 \(d_i\) 个数大于它,\(ld - d_i\) 个数小于它。
交换前有 \(D\) 中元素的逆序对有 \(\sum_{i = 1}^{lb} b_i + \sum_{j = 1}^{lc} (ld - d_i)\) 对。
交换后有 \(\sum_{i = 1}^{lb} (ld - b_i) + \sum_{i = 1}^{lc}d_i\) 对。
两式相减,得 \(2 \times \left(\sum_{i = 1}^{lb}b_i - \sum_{j = 1}^{lc}d_i\right) + ld \times(lc - lb)\) 。
因此,此部分交换偶奇偶性的变化仅与 \(ld \times (lc - lb)\) 有关。
最后考虑 \(A\) 与 \(C\) 自身逆序对的变化。
因为题目保证给出的数两两不相同,所以交换一对数逆序对的奇偶性一定变化,因此统计其交换了几次即可。
这部分答案就仅与 \(la \times lc\) 有关了。
因此逆序对数量奇偶性的变化仅与 \(la \times lc + ld \times (lc - lb)\) 有关。证毕。
那么该题就仅需要用树状数组来处理操作 3 和 操作 4 ,利用结论来处理操作 1 和操作 2 。
细节
记得开 long long
。
代码
点击查看代码
#include <iostream>
#include <cstdio>
typedef long long LL;
using namespace std;
const int N = 2e6 + 5, LEN = 2e5 + 5;
#define lowbit(x) (x & -x)
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)
LL n, m, ans;
LL a[LEN], tr[N];
void add(LL p, LL q) { for (; p <= 2e6; p += lowbit(p)) tr[p] += q; }
int query(LL p) {
LL sum = 0;
for (; p; p -= lowbit(p)) sum += tr[p];
return sum;
}
int main() {
scanf("%lld%lld", &n, &m);
LF(i, 1, n) {
scanf("%lld", &a[i]);
ans ^= query(2e6) - query(a[i]);
add(a[i], 1);
}
while (m--) {
int t, l1, r1, l2, r2;
scanf("%lld", &t);
if (t == 1) {
scanf("%lld%lld%lld%lld", &l1, &r1, &l2, &r2);
int n1 = r1 - l1 + 1, n2 = l2 - r1 - 1, n3 = r2 - l2 + 1;
ans ^= n2 * (n1 + n3) + n1 * n3;
}
else if (t == 2) {
scanf("%lld%lld%lld", &l1, &r1, &r2);
int n1 = r1 - l1 + 1, n2 = r2 - r1;
ans ^= n1 * n2;
}
else if (t == 3) {
scanf("%lld", &l1);
ans ^= query(2e6) - query(l1);
add(l1, 1);
}
else if (t == 4) {
scanf("%lld", &l1);
ans ^= query(l1);
add(l1, 1);
}
puts(ans & 1 ? "odd" : "even");
}
return 0;
}