标签:排序 788 mid long nxd 逆序 include AcWing
前备知识 归并排序
不会的点这里QwQ
逆序对算法
定义
逆序对的定义如下:对于数列的第 \(i\) 个和第 \(j\) 个元素,如果满足 \(i<j\) 且 \(a_i>a_j\),则其为一个逆序对;否则不是。
分析
用分治算法解决。
我们将序列从中间分开,将逆序对分成三类:
- 两个元素都在左边;
- 两个元素都在右边;
- 两个元素一个在左一个在右。
我们可以推出大概流程:
- 算左边的;
- 算右边的;
- 算一左一右的;
- 相加。
如何计算
我们发现一个性质:当数组分为左右两部分时,其中一个部分中的数字位置进行了交换并不会影响另外一部分与该部分之间产生的逆序对数(也就是“一左一右”的情况)。 根据归并排序流程,发现可以用归并排序排序。
有什么好处?
如果右侧指针指向的数字小于左侧指针指向的数字,那么说明左侧指针所指向的数字以及该序列之后的数字均大于右侧指针所指向的数字,所及将这些数字全部记录,ans += mid - i + 1
即可。
算法时间复杂度
与归并排序相同,复杂度 \(O(nlogn)\)。
代码实现
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 100010;
long long n, a[N], ans, r[N];
void nxd(long long s, long long t)
{
long long mid = (s + t) / 2, i = s, j = mid + 1, k = s;
if (s == t)
{
return;
}
nxd(s, mid);
nxd(mid + 1, t);
while (i <= mid && j <= t)
{
if (a[i] <= a[j])
{
r[k] = a[i], i ++ , k ++ ;
}
else
{
r[k] = a[j], j ++ , k ++ , ans += mid - i + 1; //ans累加答案,其他部分与归并排序相同,不做解释
}
}
while (i <= mid)
{
r[k] = a[i], i ++ , k ++ ;
}
while (j <= t)
{
r[k] = a[j], j ++ , k ++ ;
}
for (int i = s; i <= t; i ++ )
{
a[i] = r[i];
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
{
cin >> a[i];
}
nxd(1, n);
cout << ans;
return 0;
}
标签:排序,788,mid,long,nxd,逆序,include,AcWing 来源: https://www.cnblogs.com/FXT1110011010OI/p/16421076.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。