P1908 逆序对

目录

题目描述

输入格式

输出格式

输入输出样例

说明/提示

代码

无注释版

有注释版


题目描述

猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。

最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai​>aj​ 且 i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。

Update:数据已加强。

输入格式

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

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

输出格式

输出序列中逆序对的数目。

输入输出样例

输入 

6
5 4 2 6 3 1

输出 

11

说明/提示

对于 25% 的数据,n≤2500。

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

对于所有数据,1≤n≤5×105。

请使用较快的输入输出。

代码

无注释版

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[500010],b[500010];
int ans,n;
void gb(int l,int r){
	if(l==r) return;
	int mid=l+r>>1;
	gb(l,mid);
	gb(mid+1,r);
	int i=l,j=mid+1,k=l-1;
	while(i<=mid&&j<=r){
		if(a[i]<=a[j]){
			b[++k]=a[i];
			i++; 
		}
		else{
			b[++k]=a[j];
			j++;
			ans+=mid-i+1;
		}
	}
	while(i<=mid){
		b[++k]=a[i];
		i++;
	}
	while(j<=r){
		b[++k]=a[j];
		j++;
	}
	for(int p=l;p<=r;p++){
		a[p]=b[p];
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	gb(1,n);
	cout<<ans;
} 

有注释版

#include<bits/stdc++.h>  // 引入 C++ 的所有标准库,包含输入输出、容器、算法等
using namespace std;  // 使用标准命名空间,避免每次都加 std::

#define int long long  // 将 int 替换为 long long 类型,确保可以处理更大的数值范围

int a[500010], b[500010];  // 定义两个数组 a 和 b,分别用于存储原始序列和排序后临时数组
int ans, n;  // `ans` 用来存储逆序对的数量,`n` 为序列的长度

// 归并排序函数,用来排序并计算逆序对
void gb(int l, int r) {
    if (l == r) return;  // 如果区间只包含一个元素,则没有逆序对,直接返回

    int mid = l + r >> 1;  // 计算中间位置
    gb(l, mid);  // 递归排序左半部分
    gb(mid + 1, r);  // 递归排序右半部分

    int i = l, j = mid + 1, k = l - 1;  // `i` 指向左半部分,`j` 指向右半部分,`k` 用来控制临时数组的索引
    // 合并两部分
    while (i <= mid && j <= r) {  // 当左右两部分都没有处理完时
        if (a[i] <= a[j]) {  // 如果左部分当前元素小于等于右部分当前元素
            b[++k] = a[i];  // 将左部分元素放入临时数组
            i++;  // 左部分指针右移
        } else {  // 如果左部分当前元素大于右部分当前元素
            b[++k] = a[j];  // 将右部分元素放入临时数组
            j++;  // 右部分指针右移
            ans += mid - i + 1;  // 右部分当前元素小于左部分元素时,所有左部分未处理的元素都形成逆序对
        }
    }
    // 将剩余元素放入临时数组
    while (i <= mid) {
        b[++k] = a[i];
        i++;
    }
    while (j <= r) {
        b[++k] = a[j];
        j++;
    }
    // 将临时数组中的数据复制回原数组
    for (int p = l; p <= r; p++) {
        a[p] = b[p];
    }
}

signed main() {
    cin >> n;  // 输入序列的长度
    for (int i = 1; i <= n; i++) {  // 输入序列中的每个元素
        cin >> a[i];
    }
    gb(1, n);  // 调用归并排序函数计算逆序对
    cout << ans;  // 输出逆序对的数量
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值