P8449 [LSOT-1] 逆序对 题解

题目传送门

思路

数学,树状数组

数学

首先,观察题目,对每个操作进行分析。
发现操作 3 与操作 4 容易用树状数组来维护(关于为什么操作 3 和 操作 4 不会影响之后的 1 和 2,可以先向后看)。
而操作 2 可以转化为区间 \([l, r]\) 与区间 \([r + 1, k]\) 互换。所以仅需思考如何解决操作 1 。
(如果你不知道树状数组如何解决逆序对问题的话,可以先做做这个

题目仅询问逆序对的奇偶性,并不关心序列具体怎样。考虑交换两个区间对逆序对的奇偶性有什么影响。

有真命题:交换两序列,逆序对的奇偶性变化仅与交换的两个序列的长度有关。

QQ20240801-163723.png

证明: 如图,交换序列 \(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;
}
posted @ 2024-08-01 19:18  FRZ_29  阅读(9)  评论(0编辑  收藏  举报