题目描述

排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。(出题人真 naive)

不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为hi,我们定义一个序列的杂乱程度为:满足 hi>hj 的 (i, j) 数量。幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

输入格式

第一行为一个正整数 n,表示小朋友的数量。

第二行包含 n 个由空格分隔的正整数 h1, h2, ..., hn,依次表示初始队列中小朋友的身高。

第三行为一个正整数 m,表示交换操作的次数。

以下 m 行每行包含两个正整数ai和bi,表示交换位置ai与位置bi的小朋友。

输出格式

输出文件共 m 行,第 i 行一个正整数表示交换操作 i 结束后,序列的杂乱程度。

题解

终于来一道简单题啦!(scx: ...)

这道题,数据规模看起来不是很大。

因为是一道逆序对的题目,所以,理所当然的先归并一下啦!(当然你想用树状数组也是可以滴)

归并排序的时候,有一个小技巧可以稍稍节省一点时间(只是减少常数罢了):

在拷贝两个子序列需要归并的时候,只需要复制左半边的,右半边在原地排序,可以证明,右半边的数在排到它之前不会被覆盖,所以时间减少了一半。(然而并没有什么卵用)

然后询问的时候,设改变为 (l, r) ,如果 h[l] == h[r] 则输出原来的答案,否则,考虑在 l, r 之间的每个 i,考虑 h[i] - h[l]h[r] - h[i]

  1. 如果 h[i] - h[l]h[r] - h[i] 异号,则逆序对个数不变(h[i] 不在 (h[l], h[r]) 之间)。
  2. 如果 h[i] - h[l]h[r] - h[i] 同正,则逆序对个数加2(h[l] < h[i] < h[r])。
  3. 如果 h[i] - h[l]h[r] - h[i] 同负,则逆序对个数减2(h[l] > h[i] > h[r])。
  4. 如果 h[i] - h[l]h[r] - h[i] 一正一零,则逆序对个数加1。
  5. 如果 h[i] - h[l]h[r] - h[i] 一负一零,则逆序对个数减1。

可以看出,不管是哪一种情况,逆序对的变化量(带符号)就等于 sgn(h[i] - h[l]) + sgn(h[r] - h[i])

当然,i 不能取 r,不然 (l, r) 会被算两次。

(scx: 时空复杂度会超限吗?)

可以看到,每个询问为的,所以,整个时间复杂度为的,又 n 到 20000, m 到 2000,因而不会 TLE,MLE 是肯定不会的。

代码

#include <bits/stdc++.h>
#define sgn(i, j) ((a[i] > a[j]) - (a[i] < a[j])) // sgn(c[i] - c[j])
#define N 20034
using namespace std;

int n, q, i;
int l, r, ans = 0;
int a[N], buf[N], tmp[N];

int MergeSort(int L, int R){ // mergesort[L, R)
    if(L + 1 == R) return L;
    int M = L + R >> 1;
    MergeSort(L, M);
    MergeSort(M, R);
    int i, j, k = L;
    memcpy(tmp + L, buf + L, M - L << 2);
    for(i = L, j = M; i < M || j < R; )
        if(j >= R || (i < M && tmp[i] <= buf[j]))
            buf[k++] = tmp[i++];
        else{
            buf[k++] = buf[j++];
            ans += M - i;
        }
    return L;
}

int main(){
    scanf("%d", &n);
    for(i = 1; i <= n; i++)
        scanf("%d", a + i);
    memcpy(buf + 1, a + 1, n << 2);
    MergeSort(1, n + 1);
    printf("%d\n", ans);
    for(scanf("%d", &q); q; q--){
        scanf("%d%d", &l, &r);
        if(l > r) swap(l, r);
        if(a[l] == a[r]){
            printf("%d\n", ans);
            continue;
        }
        for(i = l; i < r; i++)
            ans += sgn(i, l) + sgn(r, i);
        swap(a[l], a[r]);
        printf("%d\n", ans);
    }
    return 0;
}

坑1:询问的时候,数组应该为原来的数组,而不是排序后的数组,因此得拷贝一份去排序。

坑2:h[l] == h[r]时,一定要输出原来的答案,否则会输出不够(导致我 WA 了一回)。