lyd读书笔记 0x05 排序(下)

终于看到了完结的曙光。。话说我规划今天做后面的题诶。。


k大数

随机选取一个数,将比它大的放在左边,小的放在右边,设有 cnt c n t 个比它小的, kcnt k ≤ c n t 就在左半段找,否则去右半段。这样递归即可,复杂度 O(n) O ( n )
万能的STL有nth_element()


分治排序与逆序对

#include <iostream>
using namespacestd;
int n, s, a[100005], t[100005], i;
void mergesort(int l, int r)
{
    if (l == r)
        return;
    int mid = (l + r) / 2;
    int p = l;
    int i = l;
    int j = mid + 1;
    mergesort(l, mid);
    mergesort(mid + 1, r);
    while (i <= mid && j <= r)
    {
        if (a[j] < a[i])
        {
            s += mid – i + 1;
            t[p] = a[j];
            p++;
            j++;
        }
        else
        {
            t[p] = a[i];
            p++;
            i++;
        }
    }
    while (i <= mid)
    {
        t[p] = a[i];
        p++;
        i++;
    }
    while (j <= r)
    {
        t[p] = a[j];
        p++;
        j++;
    }
    for (i = l; i <= r; i++)
        a[i] = t[i];
}
int main()
{
    cin >> n;
    for (i = 1; i <= n; i++)
    cin >> a[i];
    mergesort(1, n);
    cout << s << endl;
    return 0;
}

觉不觉得这个代码有点熟悉。。
其实这个代码是2017初赛程序填空T3的代码。。原封不动的拿过来了。。

原理这里稍微解释一下吧,重点是merge的部分。
首先我们考虑一下,如果不进行求逆序对,那么我们的过程是怎样的。其实很简单,我们建两个指针然后滚一遍,碰到下一个数就比较大小。显而易见的这个时候我们的两个子数组是已经排序完成的,所以直接维护即可。当然如果到最后发现还有一个数组有剩余,就直接放在最后。

那么我们考虑哪些地方对逆序对有贡献。显而易见的,一个是子数组内部,一个是两个数组之间。但是我们知道,对于一个递归过程而言,子数组内部的一定已经处理完毕了,所以只需要考虑两个子数组之间的贡献,那做法就很显然了,如果第二个数组的元素比第一个数组的某个元素要小,就意味着这个元素比第一个数组剩余元素都要小,所以我们加上 midi+1 m i d − i + 1 个元素。

当然顺序对也是同理的,我们考虑第一个数组对第二个数组的贡献即可。这样的复杂度是 O(nlogn) O ( n l o g n ) 的。

模板题:POJ2299,记得开LL。。

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

#define N 500005
int a[N], c[N], t;
long long cnt;

void merge(int l, int r) {
    if(l == r) return;
    int mid = l + r >> 1;
    merge(l, mid);
    merge(mid + 1, r);
    int i = l, j = mid + 1, p = l;
    while(i <= mid && j <= r) {
        if(a[i] > a[j]) {
            cnt += mid - i + 1;
            c[p] = a[j]; 
            ++j; ++p;
        } else {
            c[p] = a[i];
            ++i; ++p;
        }
    }
    if(i <= mid) 
        for(int k = i; k <= mid; ++k)
            c[p++] = a[k];
    if(j <= r)
        for(int k = j; k <= r; ++k)
            c[p++] = a[k];
    for(int k = l; k <= r; ++k) a[k] = c[k];
} 

int main() {
    int n;
    while(~scanf("%d", &n)) {
        if(n == 0) break;
        for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        merge(1, n);
        printf("%lld\n", cnt);
        cnt = 0;
    }
    return 0;
}

奇数码先打lazytag了。。。

新blog:http://lire.yuyuko.cc ,不过似乎不是很稳定的样子qwq

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值