归并排序求逆序对--分治(洛谷P1908)

题目链接https://www.luogu.org/problem/P1908

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
Update:数据已加强。

输入格式

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。序列中每个数字不超过10^9109

输出格式

给定序列中逆序对的数目。

输入输出样例

输入 

6
5 4 2 6 3 1

输出

11

说明/提示

对于25%的数据,n≤2500

对于50%的数据,n≤4×10^4。

对于所有数据,n≤5×10^5

请使用较快的输入输出

应该不会n方过50万吧 by chen_zhe


简单讲一下吧,归并排序就是将区间[l,r]分成[l,mid]和[mid+1,r]然后用另外一个数组将他们有序地记录下来即一直记录最小值就好了,接着我们把这个有序的数组赋值给它,然后它再传递给上一层。由于[l,mid]是有序的,那么如果在[mid+1,r]中找到小于前面区间的数的话,逆序对的个数就是mid-p+1(其中p是[l,mid]中的一个位置):

int ll=l,rr=mid+1;
while (ll<=mid && rr<=r) {
	if (a[ll]<=a[rr]) {
		b[++cnt]=a[ll];
		ll++;
	} 
	else {
		ans+=(mid-ll+1);
		b[++cnt]=a[rr];
		rr++;
	}
}

很明显的是归并排序的常数比线段树小得多,跑到非常快。

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

const int mac=5e5+10;

int a[mac],b[mac];
long long ans=0;

void cdq(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);
    int ll=l,rr=mid+1;
    for (int i=l; i<=r; i++) b[i]=a[i];
    int cnt=0;
    while (ll<=mid && rr<=r){
        if (a[ll]<=a[rr]) {
            b[++cnt]=a[ll];
            ll++;
        }
        else {
            ans+=(mid-ll+1);
            b[++cnt]=a[rr];
            rr++;
        }
    }
    while (ll<=mid) b[++cnt]=a[ll++];
    while (rr<=r) b[++cnt]=a[rr++];
    cnt=0;
    for (int i=l; i<=r; i++) a[i]=b[++cnt];
}

void in(int &x)
{
    char ch=getchar();
    int f=0;
    while (ch>'9' || ch<'0') ch=getchar();
    while (ch<='9' && ch>='0') f=(f<<3)+(f<<1)+ch-'0',ch=getchar();
    x=f;
}

void out(long long x)
{
    if (x>=10)
        out(x/10);
    putchar(x%10+'0');
}

int main()
{
    int n;
    in(n);
    for (int i=1; i<=n; i++) 
        in(a[i]);
    cdq(1,n);
    out(ans);
    putchar('\n');
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值