题意:给一个序列 a1, a2, ..., an,求这个序列的以下n中排列中,逆序数和的最小值。
思路:先用线段树求出初始序列的逆序数和,再递推的求其他排列的逆序数和。
1、建立一颗线段树,初始整个inv[i]为0;
2、更新线段树,统计x[i]至n-1中已经插入了的数的个数和,并做相应的更新。
3、根据初始序列的逆序数的和,递推式的求其他排列下的逆序数的和的最小值。
递推公式: sum += (n-x[i]-1) - x[i] ; 即 后一个逆序数和 = 前一个 逆序数和 + 比在首位的数大的数的个数 - 比在首位的数小的数的个数
#include <stdio.h>
#define maxn 5555
#define lson l , m , rt*2
#define rson m+1 , r , rt*2+1
int inv[maxn*4];
int x[maxn];
int min(int x,int y)
{
return x < y ? x : y;
}
void PushUp(int rt){
inv[rt] = inv[rt*2] + inv[rt*2+1];
}
void build(int l,int r,int rt)
{
inv[rt] = 0;
if(l == r) return ;
int m = (l + r) / 2;
build(lson);
build(rson);
}
int query(int L,int R,int l,int r,int rt)
{
if(L <= l && r <= R){
return inv[rt];
}
int m = (l + r) /2;
int ret = 0;
if(L <= m) ret += query(L,R,lson);
if(R > m) ret += query(L,R,rson);
return ret;
}
void update(int index,int l,int r,int rt)
{
if(l == r){
inv[rt] = 1;
return ;
}
int m = (l + r) / 2;
if(index <= m) update(index,lson);
else update(index,rson);
PushUp(rt);
}
int main()
{
int n,i,sum;
while(scanf("%d",&n)==1)
{
build(0,n-1,1);
sum=0;
for(i = 0; i < n; i ++){
scanf("%d",&x[i]);
sum += query(x[i],n-1,0,n-1,1);
update(x[i],0,n-1,1);
}
int ans = sum;
for(i = 0; i < n ; i ++)
{
sum += n - 2 * x[i] - 1;
ans = min(ans,sum);
}
printf("%d\n",ans);
}
}