zoukankan      html  css  js  c++  java
  • [NOI Online 提高组]冒泡排序

    传送门

      怎么说呢(咳咳),貌似是一道结论题(博主并不知道结论呀,所以就FST抱灵了呀)。好吧博主承认没看到(k<2^{31})(相信没阿克的奆佬[除我]应该都RE在了这里)。测试成绩出来了,还好没有(k)极其之大的情况:)

      先强调这里的({p_i})排列

      仔细研读这个代码:

    // 一轮交换
    for i = 1 to n-1
        if p[i] > p[i + 1]
            swap(p[i], p[i + 1])
    

      好像跟我刚学OI时的冒泡不一样耶

      相邻的两项如果是逆序对就会进行交换。显然交换次数就是逆序对个数,不过这个没什么卯用。

      再康康:发现如果一个数很大,比它后面的xx个数都要大,那么一顿排序猛如虎后,这个数会到达第一个比它大的数的前面,比如说({mathring4,1,2,3,5}),一轮排序后变成了({1,2,3,mathring4,5})。计(f_i)表示数字(i)所在位置前面比它大的数,那么逆序对总数就是(sum f_i)。这轮排序结束后,显然(f_i)只减不增,并且最多只会减一,就打这个例子,原本(f_1=f_2=f_3=1),之后就全变成了(0)。事实上由于前面最多有一个数字经过这个位置,且这个数字一定大于这个位置的数字,故最多只能使(f_i)减一,这个情况的发生必定有前面比它大的数字经过它。而又如果(f_i>0),则前面必定有比它大的数,其中一个数字一定会经过它,而且这个数字一定是前缀最大的数字(根据前文的推断,所有比这个数字小的都被“堵在”这个最大数字前面)。

      把这些信息提炼出来就是:对于数字(i),排序过程中如果它所在位置前面有数字大于(i),那么一轮排序会使得前面最大的数字排到(i)的后面,这样(f_i)会减一。

      综上,对于数字(i),其(f_i)(k)轮排序后会变成(max{f_i-k,0})

      然后这道题就简单啦。维护(f_i),交换两个数相当于对(f_i)修改(本题的修改非常简单),对于查询,实际上就是求(sumlimits_{1leqslant ileqslant n}max{f_i-k,0}),我们可以运用差分,配上数据结构维护关于(k)的信息:前缀(f_i)和前缀(g_i=sumlimits_{1leqslant jleqslant n}[f_j==i])(g_i)(f_j==i)的个数),然后查询答案对(f_i)进行差分再去掉(g_i)的差分乘上(k)即可。

      复杂度( ext{O}(nlog n))

      为什么说(k)很坑?你如果做(2^{31}-1)次排序,再查查看(k)前缀(⊙o⊙)?!

  • 相关阅读:
    markdown keynote
    pyecharts
    运行成功
    python发邮件3
    python发邮件2
    python发邮件1
    python发邮件
    python中的编码声明
    auther tonyxiao
    111
  • 原文地址:https://www.cnblogs.com/ac-evil/p/12438965.html
Copyright © 2011-2022 走看看