逆序对2.0

DescriptionDescription


我们称逆序对为一个序列中满足 i<ji<jai>aja_i > a_j​ 的二元组 (i,j)(i,j)

若一个排列的逆序对个数为奇数,则称它为一个奇排列,否则它被称为偶排列。

给出一个长度为 nn 的排列,有 44 种操作:

  • 1 l r\color{Red}1\ l \ r 交换 ala_l​ 和 ara_r​。
  • 2 l r\color{Red}2 \ l \ r 翻转区间 [l,r][l,r] 内的数。
  • 3 l r k\color{Red} 3 \ l \ r \ k 将区间 [l,r][l,r] 内的数左移 kk 位。
  • 4 l r k\color{Red}4 \ l \ r \ k 将区间 [l,r][l,r] 内的数右移 kk 位。

对一个长度为 n 的序列的操作定义如下:

  1. 翻转\color{Red}翻转 对于所有 lirl≤i≤r ,将第 ii 个位置的数移动到第 l+ril+r-i 个位置。
  2. 左移k\color{Red}左移 k 位 对于所有 l<irl<i≤r ,将第 ii 个位置的数移动到第 i1i−1 个位置,第 ll 个位置的数移动到第 rr 个位置。重复此操作 kk 次。
  3. 右移k\color{Red}右移 k 位 对于所有 li<rl≤i<r,将第 ii 个位置的数移动到第 i+1i+1 个位置,第 rr 个位置的数移动到第 11 个位置。重复此操作 kk 次。

注意上述定义中同一个操作内的移动都是同时进行的。

小 T小 \ T 想要知道每次操作之后这个排列是奇排列还是偶排列。

InputInput


第一行两个整数 n,mn,m ,表示序列的长度和操作的次数。

第二行 nn 个整数,表示一个 11nn 的排列。

接下来 mm 行,每行三或四个整数,表示一次操作,具体格式及其含义见题目描述。

OutputOutput


mm 行,表示每次操作后的答案,若为奇排列,则输出 11 , 若为偶排列则输出 00

Sample InputSample \ Input


样例一:

5 5
1 2 3 4 5
1 1 3
1 2 3
2 2 5
3 1 2 1
4 3 5 1

样例二:

10 10
1 4 3 5 2 9 8 6 7 10
1 2 3
2 3 5
4 3 7 2
2 3 5
3 5 7 4
1 2 6
4 1 10 6
3 2 4 3
1 3 4
2 3 9

Sample OutputSample \ Output


样例一:

1
0
0
1
1

样例二:

0
1
1
0
0
1
1
1
0
1

HINTHINT


样例一说明:

原排列的逆序对个数为 00

第一次操作后序列为:3 2 1 4 5\boxed{3 \ 2 \ 1 \ 4 \ 5} ,逆序对个数为 33 ,故输出 11

第二次操作后序列为:3 1 2 4 5\boxed{3 \ 1 \ 2 \ 4 \ 5} ,逆序对个数为 22 ,故输出 00

第三次操作后序列为:3 5 4 2 1\boxed{3 \ 5 \ 4 \ 2 \ 1} ,逆序对个数为 88 ,故输出 00

第四次操作后序列为:5 3 4 2 1\boxed{5 \ 3 \ 4 \ 2 \ 1} ,逆序对个数为 9,故输出 11

第五次操作后序列为:5 3 1 4 2\boxed{5 \ 3 \ 1 \ 4 \ 2} ,逆序对个数为 7,故输出 11

数据范围:

对于前 20 %20 \ \% 的数据,n100m10n≤100,m≤10

对于前 40 %40 \ \% 的数据,n103m103n≤103,m≤10^3

对于前 60 %60 \ \% 的数据,n103m5×105n≤10^3,m≤5×10^5

对于 100 %100 \ \% 的数据,1m5×1051lrn1kn2×1051≤m≤5×10^5,1≤l≤r≤n,1≤k≤n≤2×10^5

解题思路解题思路

结论:任意交换两个数 ai 和 aj ,且 aiaj ,则逆序对数的奇偶性必定改变\color{Red}结论:任意交换两个数 \ a_i \ 和 \ a_j \ ,且 \ a_i \ne a_j \ ,则逆序对数的奇偶性必定改变


证明

i)i) 先研究一下相邻两个元素的对换

考虑序列

pijqp \dots ij \dots q

(i,j)(i,j) 对换后

pjipp \ldots ji \ldots p

我们可以知道, iijj 对换后,并不影响 ppiippjjiiqqjjqq 的大小顺序排列,只有 iijj 之间的大小排列发生了改变,假设 iijj 之间的排列为顺序,则对换后,变为逆序,逆序数加 11 ,改变奇偶性,若 iijj 之间的排列为逆序,则对换后,变为顺序,逆序数减 11 , 改变奇偶性

ii)ii) 现在我们来考虑一下一般情况

ik1ksj\ldots ik_1 \ldots k_sj \ldots

经过 (i,k1),(i,k2)(i,ks)(i,k_1),(i,k_2) \ldots (i,k_s)ss 次对换后得到

k1k2ij\ldots k_1k_2 \ldots ij \ldots

在经 (i,j)(i,j)11 次对换后得到

k1k2ji\ldots k1k2 \ldots ji \ldots

又经过 (j,ks),(j,ks1)(j,k1)(j,k_s),(j,k_s-1) \ldots (j,k_1)ss 次对换后得到

jk1ksi\ldots jk_1 \ldots k_si \ldots

合计共经过 2s+12s+1 次对换,显然 2s+12s+1 为奇数

由引理 11 可以知道,奇数次对换改变排列奇偶性

\therefore 任意交换两个数 ai 和 aj ,且 aiaj ,则逆序对数的奇偶性必定改变任意交换两个数 \ a_i \ 和 \ a_j \ ,且 \ a_i \ne a_j \ ,则逆序对数的奇偶性必定改变

证毕


解题步骤如下:

先求出原始序列的逆序对数的奇偶性(可以通过归并排序或者树状数组的方法求逆序对数)

操作1. 交换 11 次( llrr 不相等) 操作2. 对半交换,相当于 len/2len/2 次操作 11 操作3. 依次向左(或向右)逐位交换。此时操作的序列长度为 len=rl+1len=r-l+1 ,那么相当于做 klen1k*(len-1) 次操作 11

这里拓展一个知识点:假设整个序列向右移 kk 位,相当于将前 nkn-k 位翻转,对后 kk 位翻转,最后对整个序列翻转。

执行上述 33 步即可,这 33 步的顺序可以任意,这样处理的优势是时间复杂度控制在 O(n)O(n) ,并且不需要另开空间,直接在原数组上就可以完成。

AC codeAC \ code

#include <bits/stdc++.h>
#define len b - a + 1
using namespace std;

inline int read()
{
    char c = getchar();
    int x = 0;
    bool f = 0;
    for (; !isdigit(c); c = getchar())
    {
        f ^= !(c ^ 45);
    }
    for (; isdigit(c); c = getchar())
    {
        x = (x << 1) + (x << 3) + (c ^ 48);
    }
    if (f)
    {
        x = -x;
    }
    return x;
}

inline void write(int x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x > 9)
    {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

int n, m;
int c[700005];
int d[700005];
int ans;

inline int lowbit(int x)
{
    return (x & -x);
}

inline void add(int x)
{
    for (; x <= n; x += lowbit(x))
        c[x]++;
}

inline int sum(int x)
{
    int s = 0;
    for (; x; x -= lowbit(x))
        s += c[x];
    return s;
}

int get()
{
    int res = 0;
    for (int i = n; i >= 1; i--)
        add(d[i]), res += sum(d[i] - 1);
    return res & 1;
}
signed main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> d[i];
    ans = get();
    for (int i = 1, z, a, b, c; i <= m; ++i)
    {
        z = read();
        a = read();
        b = read();
        if (a > b)
            swap(a, b);
        if (z == 1)
        {
            if (a != b)
                ans ^= 1;
        }
        else if (z == 2)
        {
            if ((len >> 1) & 1)
                ans ^= 1;
        }
        else if (z == 3)
        {
            c = read();
            if ((c * (len - 1)) & 1)
                ans ^= 1;
        }
        else
        {
            c = read();
            if ((c * (len - 1)) & 1)
                ans ^= 1;
        }
        write(ans);
        putchar('\n');
    }
}
posted @ 2021-06-22 21:51  蒟蒻orz  阅读(13)  评论(0编辑  收藏  举报  来源