本题的意思是求逆序数,可以将前m个数放到序列最后面,求逆序数的最小值
方法就是找出每个数之前有多少个比它大的数累加即可,处理时可以用树状数组,也可以用线段树,我用的是线段树
先建一棵空树,依次处理每个元素,把线段树的对应节点a[i]++,然后在原序列中,在a[i]前面且比它大的就是在线段树中
a[i+1]到n这段区间和了;
这样容易找原序列的逆序数,至于调动是有个规律的,每次把第一个移到后面,逆序数就等于原来的-(后面比a小的)+(比a大的)
代码如下
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
int a[5005];
int tree[5005*4];
void build(int n,int l,int r)
{
if(l==r)
{
tree[n]=0;
return;
}
int m=(l+r)>>1;
build(n<<1,l,m);
build(n<<1|1,m+1,r);
tree[n]=tree[n<<1]+tree[n<<1|1];
}
void update(int n,int l,int r,int pos)
{
if(l==r)
{
tree[n]++;
return;
}
int m=(l+r)>>1;
if(pos<=m)
update(n<<1,l,m,pos);
else
update(n<<1|1,m+1,r,pos);
tree[n]=tree[n<<1]+tree[n<<1|1];
}
int query(int n,int l,int r,int ll,int rr)
{
if(ll<=l&&rr>=r)
{
return tree[n];
}
int ans=0;
int m=(l+r)>>1;
if(ll<=m)
ans+=query(n<<1,l,m,ll,rr);
if(rr>m)
ans+=query(n<<1|1,m+1,r,ll,rr);
return ans;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
a[i]++;
}
build(1,1,n);
int cnt=0;
for(int i=1; i<=n; i++)
{
update(1,1,n,a[i]);
cnt+=query(1,1,n,a[i]+1,n);
}
int mx=cnt;
for(int i=1; i<=n; i++)
{
cnt=cnt-(a[i]-1)+(n-a[i]);
mx=min(mx,cnt);
}
printf("%d\n",mx);
}
return 0;
}