[NOI Online #1 提高组]冒泡排序

Luogu P6185 NOI Online #1 冒泡排序

首先定义\(f(i)\)表示序列中\(i\)位置前有多少个比他大的数。

根据冒泡排序的过程很容易发现一些\(f(i)\)的特点:

  • 每经过一轮冒泡排序,由\(f(i)\)构成的序列就会整体减一并且向左移动一位。
  • 已经为\(0\)\(f(i)\)不会发生变化。

举一个例子。

原序列:

\(a_i\) 3 5 4 2 1
\(f(i)\) 0 0 1 3 4

第一轮排序:

\(a_i\) 3 4 2 1 5
\(f(i)\) 0 0 2 3 0

第二轮排序:

\(a_i\) 3 2 1 4 5
\(f(i)\) 0 1 2 0 0

第三轮排序:

\(a_i\) 2 1 3 4 5
\(f(i)\) 0 1 0 0 0

相信到了这里规律已经很明显了。

一个小结论:如果一个位置的逆序对为\(k\),那么它在第\(k\)轮时的对答案的贡献就消失了。

那么可以这么想,考虑用树状数组维护第\(k\)轮排序的逆序对个数,遇到操作\(1\)时做对应的修改就可以了。

接下来就在于,应该如何去维护呢?

我们知道交换只会影响一个位置的逆序数,那么直接修改就可以了。

//代码缩进被VSCode强行格式化了……
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
long long ans, tree[200005], b[200005], p[200005], cnt[200005], t, k, n, m;
long long lowbit(long long x)
{
    return x & -x;
}
void add(long long x, long long val)
{
    for (long long i = x; i <= n; i += lowbit(i))
        tree[i] += val;
}
long long query(long long x)
{
    long long ret = 0;
    for (long long i = x; i > 0; i -= lowbit(i))
        ret += tree[i];
    return ret;
}
int main()
{
    scanf("%lld%lld", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &p[i]);
        add(p[i], 1);
        b[i] = query(n) - query(p[i]);
        ans += b[i];//记录初始逆序数。
        cnt[b[i]]++;
    }
    memset(tree, 0, sizeof(tree));
    add(1, ans);
    int tmp = 0;
    //这里把第一轮视为原序列,第二轮实际上是冒泡排序第一轮。
    for (int i = 2; i <= n + 1; i++)
    {
        tmp += cnt[i - 2];//这一些不需要减,因为贡献已经为0了
        add(i, -(n - tmp));//其他的贡献全都要-1
    }
    for (int i = 1; i <= m; i++)
        {
            scanf("%lld%lld", &t, &k);
            if (t == 1)
            {
                if (p[k] < p[k+1])
                {
                    add(1, 1);
                    b[k]++;
                    add(b[k] + 2, -1);
                }
                else
                {
                    add(1, -1);
                    b[k + 1]--;
                    add(b[k + 1] + 2, 1);
                }
                swap(p[k], p[k + 1]);
                swap(b[k], b[k + 1]);
            }
            else 
            {
                k = min(n - 1, k);
                printf("%lld\n",query(k + 1));
            }
    }
    return 0;
}
posted @ 2020-05-04 20:33  Nanjo  阅读(195)  评论(0编辑  收藏  举报