7622:求排列的逆序数 题解
首先看见题目后先想到的当然是O(nlogn)的暴力算法,于是第一码诞生:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define SIZE_ 100001
unsigned long long n,i,j,value[SIZE_],anss;
int main()
{
for(cin>>n;i<n;i++)
{
cin>>value[i];
for(j=0;j<i;j++)
if(value[j]>value[i])
anss++;
}
cout<<anss;
return 0;
}
很简洁的代码,绝对无错但是时效过低,T了四个点
然后开始思考怎么才能避免重复判断比较大小记录结果?
简单的不等式性质:若 a>b,b>c 则 a>c
有点分治的味道了
所以说当确定一个数它左边比它大的数一定比它右边比它小的数大(听上去很拗口建议先考虑断句再读>.<)
分治算法是要两侧的计算互不影响,那我们就要考虑以下情况: 有数列a,b,c,其中$a>b,b<c,a>c$的情况呢??
只要在比较之后交换a,b(或b,c)就可以不重不漏地记录下对答案的贡献
不难看出,这个过程与二分排序相似,不过此处求的是操作数而不是结果的有序数列(我们已经知道的1~n)
为了解释相应的算法,我们来举个栗子(真香) :
求该排列中的逆序数
流程表如下:
那么根据以上描述就可以得到下面这串代码:
#include<iostream>
using namespace std;
#define SIZE_ 100001
unsigned long long n,i,j,
value[SIZE_],anss;
void divcount(unsigned int l,unsigned int r)
{
if(l==r)
return;
register unsigned int mid=(l+r)/2,i=l,j=mid+1;
divcount(l,mid);
divcount(mid+1,r);
unsigned int k=0,temp[r-l];
while(i<=mid&&j<=r)
if(value[i]<=value[j])
temp[++k]=value[i++];
else
{
temp[++k]=value[j++];
anss+=mid-i+1;
}
while(i<=mid)temp[++k]=value[i++];
while(j<= r )temp[++k]=value[j++];
for(i=1;i<=k;i++)
value[i+l-1]=temp[i];
return;
}
int main()
{
for(cin>>n;i<n;i++)
cin>>value[i];
divcount(0,n-1);
cout<<anss;
return 0;
}
啊哈AC!
由于这是本人的第一篇博文,在最后附上创作声明:
创作声明
- 首先,本博主对计算机科学、数学等方面学术略有研究,并希望更好的利用业余时间,写些又实在意义的文字,所以就有了第二条
(滑稽)。 - 如果看到此声明的你有什么学术上的疑惑,私信博主,如果问题在博主的水平范围内即会推出相应的解题报告来解答,也顺便谈谈博主对此问题的看法。
- 如果文章存在错误还请见谅,并且请立即私信博主,博主将会在第一时间给予改正。
- 有什么好的建议(像“不喜欢这么不正经的文风”之类还是藏在心里别告诉我比较好,嘻嘻)或是意见都欢迎私信博主哦(>.<)!