题目描述
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 a_i>a_jai>aj 且 i<ji<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
Update:数据已加强。
输入格式
第一行,一个数 nn,表示序列中有 nn个数。
第二行 nn 个数,表示给定的序列。序列中每个数字不超过 10^9109。
输出格式
输出序列中逆序对的数目。
输入输出样例
输入 #1复制
6 5 4 2 6 3 1
输出 #1复制
11
说明/提示
对于 25\%25% 的数据,n \leq 2500n≤2500
对于 50\%50% 的数据,n \leq 4 \times 10^4n≤4×104。
对于所有数据,n \leq 5 \times 10^5n≤5×105
请使用较快的输入输出
应该不会 O(n^2)O(n2) 过 50 万吧 by chen_zhe
来自 <https://www.luogu.com.cn/problem/P1908?contestId=46258>
解题思路:
利用归并排序思路,
将数组不断的分割,直至数组的长度为1
开始进行合并;合并的过程中总是前后两个数组直接进行元素的排序
import java.io.*;
public class Main {//psvm:自动补main
public static long count = 0; //全局变量,使用合并排序,计算逆序对数
public static void main(String[] args) throws IOException {
BufferedReader re = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer r = new StreamTokenizer(re);
PrintWriter pr = new PrintWriter(new OutputStreamWriter(System.out));
r.nextToken();//缓冲,识别回车
int length = (int) r.nval;//序列长度
int order[] = new int[length];
for (int i = 0; i < length; i++) {
r.nextToken();
order[i] = (int) r.nval;
}
getReverseCount(order);
pr.println(count);
pr.flush();
}
//使用归并排序方法计算数组A中的逆序对数
public static void getReverseCount(int[] A) {
if (A.length > 1) {//递归的限制条件
int[] leftA = getHalfArray(A, 0); //数组A的左半边元素
int[] rightA = getHalfArray(A, 1); //数组A的右半边元素
getReverseCount(leftA);
getReverseCount(rightA);
mergeArray(A, leftA, rightA);
}
}
//根据judge值判断,获取数组A的左半边元素或者右半边元素
public static int[] getHalfArray(int[] A, int judge) {
int[] result;
if (judge == 0) { //返回数组A的左半边
result = new int[A.length / 2];
for (int i = 0; i < A.length / 2; i++)
result[i] = A[i];
} else { //返回数组的右半边
result = new int[A.length - A.length / 2];
for (int i = 0; i < A.length - A.length / 2; i++)
result[i] = A[A.length / 2 + i];
}
return result;
}
//合并数组A的左半边和右半边元素,并按照非降序序列排列
public static void mergeArray(int[] A, int[] leftA, int[] rightA) {
int len = 0;
int i = 0;
int j = 0;
int lenL = leftA.length;
int lenR = rightA.length;
while (i < lenL && j < lenR) {
if (leftA[i] > rightA[j]) {
A[len++] = rightA[j++]; //将rightA[j]放在leftA[i]元素之前,那么leftA[i]之后lenL - i个元素均大于rightA[j]
count += (lenL - i); //合并之前,leftA中元素是非降序排列,rightA中元素也是非降序排列。所以,此时就新增lenL - i个逆序对
} else {
A[len++] = leftA[i++];
}
}
while (i < lenL)
A[len++] = leftA[i++];
while (j < lenR)
A[len++] = rightA[j++];
}
}