●虽然是一个比较小的简单算法,但是我今天才学会2333可能真的是太垃圾了。。没关系 总结一下:
板子:
codevs 1688 求逆序对
题面就不写了,数据范围1e5(数字数量和数字大小) 所以要开long long了
一开始没开long long然后炸了
这样子:
我最想说的,其实是在归并排序时,如何记录答案:
一开始我是这样想的:
对于要合并的有序序列A,B(A是原数列的左区间,B是右区间)如:
–>A:4 5 6
–>B:2 3 7 8
记两区间的开头两个数字分别为i,j
现在i和j分别是4,2,现在我要合并,j < i,这两个肯定是逆序对啦,那么我的答案应该怎么加呢?
我的错误的想法:
–>每次找到一组j < i时,那么j之前已经pop掉的那些数字一定 < i,所以我的答案应该是ans+=j-mid(j-(mid+1)+1),但我试了一下样例,发现输出了6,而答案应该是3,这样子肯定不对了。
为什么不对呢?
现在继续往下pop,我pop掉2,下一个数字是3,现在j=3,但是i=4没有变,我再次记录答案时,又把2加进去了 ,所以逆序对在这种情况下被重复记录了,显然是不对的。
那么我们应该怎样记录呢?
换一个思路–>
–>每次找到一组i,j,那么i以后的所有没有pop过的数字都和j组成逆序对,这时候,即使出现上述情况,答案也不会重复记录,也就是:ans+=mid-i+1;
这样子问题就解决啦~
代码很简单,递归实现merge_sort
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn=1e5+10;
ll n,ans=0;
ll a[maxn],t[maxn];
void done_sort(ll l,ll r,ll mid)
{
ll i=l,j=mid+1;
ll k=0;
while(i<=mid&&j<=r)
{
if(a[i]<=a[j]) t[++k]=a[i++];
else
{
t[++k]=a[j++];
ans+=mid-i+1;
}
}
while(i<=mid) t[++k]=a[i++];
while(j<=r) t[++k]=a[j++];
for(ll num=1;num<=k;++num)
a[num+l-1]=t[num];
}
void merge_sort(ll l,ll r)
{
if(l<r)
{
ll mid=(r+l)>>1;
merge_sort(l,mid);
merge_sort(mid+1,r);
done_sort(l,r,mid);
}
}
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
merge_sort(1,n);
printf("%lld",ans);
return 0;
}
记得一定要开long long!!