题目链接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;
}