逆序对 【luogu P1908】C++题解

本文介绍了如何利用归并排序和树状数组解决关于给定正整数序列中逆序对数量的问题。归并排序方法的时间复杂度为O(nlogn),而树状数组则提供了更直接且时间复杂度也为O(nlogn)的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看题目:

逆序对

题目描述

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

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

Update:数据已加强。

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

6
5 4 2 6 3 1

样例输出 #1

11

提示

对于 25%25\%25% 的数据,n≤2500n \leq 2500n2500

对于 50%50\%50% 的数据,n≤4×104n \leq 4 \times 10^4n4×104

对于所有数据,n≤5×105n \leq 5 \times 10^5n5×105

请使用较快的输入输出

应该不会 O(n2)O(n^2)O(n2) 过 50 万吧 by chen_zhe

---------------------一条正义的分割线---------------------------

根据我两年半单身练习的exp,做题第一眼先看提示,分析算法时间复杂度。

对于所有数据,n≤5×105n \leq 5 \times 10^5n5×105

得出代码时间复杂度∈[O(n),O(n2))\in [O(n),O(n^2))[O(n),O(n2))

又双分析O(n)的时间复杂度不太可能(for循环遍历a1,…,ana_1,\dots,a_na1,,an,每个数还要进行>O(1)>O(1)>O(1)的操作)。

得出时间复杂度≈O(logn)\approx O(logn)O(logn)

解法一:归并排序

由归并的步骤可联想到使用归并排序时判断,代码如下。

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
long long n,a[500005],cnt;
void Msort(int l,int r){
	if(l==r)return;
	int mid=(l+r)/2;
	Msort(l,mid);
	Msort(mid+1,r);
	int *b=new int[r+1],k=l,i=l,j=mid+1;
	while(i<=mid&&j<=r){
		//TODO
		if(a[i]<=a[j])b[k]=a[i],i++,k++;
		else{
			b[k]=a[j];k++;j++;
			cnt+=mid-i+1;
		}
	}
	while(i<=mid)b[k]=a[i],i++,k++;
	while(j<=r){
		b[k]=a[j],j++,k++;
	}
	for(int i=l;i<=r;i++){
		//TODO
		a[i]=b[i];
	}
	delete[]b;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	Msort(1,n);
	cout<<cnt;
	return 0;
}

代码简单,经分析,时间复杂度为O(nlogn)O(nlogn)O(nlogn)

归并算法AC

解法二:树状数组

怎样想到树状数组捏?
考虑根据值来建树状数组 , 初始树状数组为全 0。现在按照序列从左到右将数据的值对应的位置的数加一,代表又有一个数出现。因此,在循环到第 i 项时,前 i−1 项已经加入到树状数组内了 , 树状数组内比 aia_iai大的都会与aia_iai 构成逆序对,因为它们一定出现的更早,所以产生的逆序对数量为i−query(ai)i-query(a_i)iquery(ai)

so code:

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int n,handle[1000005];
struct node{
	int num,rank;
	bool operator<(const node&b)const{
		if(num==b.num)return rank<b.rank;
		return num<b.num;
	}
}a[1000005];
int tree[1000005]; 
int lowbit(int x){
	return x&(-x);
}
void insert(int p,int d){
	while(p<=n){
		tree[p]+=d;
		p+=lowbit(p); 
	}
}
int query(int p){
	int sum=0;
	while(p){
		sum+=tree[p];
		p-=lowbit(p);
	}
	return sum;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].num;
		a[i].rank=i;
	}sort(a+1,a+1+n);
	for(int i=1;i<=n;i++){
		handle[a[i].rank]=i;
	}
	int res=0;
	for(int i=1;i<=n;i++){
		insert(handle[i],1);
		res+=i-query(handle[i]);
	}
	cout<<res<<endl;
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值