求一个排列的最小逆序数,只能够通过将第一个数字不断后移的方式更改顺序。由于题目给出的数字是0-n-1所以每次将首个数字向后移时都满足(假设逆序数为ans首个数字为a[e])ans加n-1-a[e]个逆序数再减去a[e]个逆序数(数学问题,那么问题就很简单了,先求出初始逆序数,再用公式不断计算,得出最小值。关键在于怎么求初始逆序数。用线段树,每次输入数字,将该数字叶子节点数更新1,并求输入这个数字时前面已有的叶子节点(即首顺序中这个数字前的数字)比该数字大的数不断叠加即可得到逆序数。
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,m;
char c[10];
const int maxn=50005;
int node[maxn<<2];
int t[maxn];
void pushup(int nd)
{
node[nd]=node[nd<<1]+node[nd<<1|1];
}
void build(int l,int r,int nd)
{
if(l==r)
{node[nd]=0;
return;}
int mid=(l+r)>>1;
build(l,mid,nd<<1);
build(mid+1,r,nd<<1|1);
pushup(nd);
}
void update(int pos,int l,int r,int nd)
{
if(l==r)
{
node[nd]=1;
return;
}
int mid=(l+r)>>1;
if(mid<pos)update(pos,mid+1,r,nd<<1|1);
else update(pos,l,mid,nd<<1);
pushup(nd);
}
int query(int s,int e,int l,int r,int nd)
{
if(s<=l&&e>=r)
{
return node[nd];
}
int ans=0;
int mid=(l+r)>>1;
if(s<=mid)ans+=query(s,e,l,mid,nd<<1);
if(e>mid)ans+=query(s,e,mid+1,r,nd<<1|1);
return ans;
}
int main()
{
while(cin>>n)
{ build(0,n-1,1);
int ans=0;
for(int i=0;i<n;i++)
{cin>>t[i];
ans+=query(t[i]+1,n-1,0,n-1,1);
update(t[i],0,n-1,1);}
int minn=ans;
for(int i=0;i<n-1;i++)
{
ans+=n-1-t[i]*2;
minn=min(minn,ans);
}
cout<<minn<<endl;
}
}