题目大意:给定数列a[1]、a[2]··········a[n],每次将移动首项移动到末尾,每移动一次,计算数列的逆序数,在一系列移动之后,求最小的逆序数。
分析:
首先求出输入序列的逆序数,然后每次取其与移动一次之后的逆序数的最小值。n-1表示最大数,n-1 - x[i]表示首项x[i]移动到数列末尾后会产生几个逆序,但是当x[i]作为首项时,会有x[i]个逆序,因此,n-1-x[i]-x[i]表示移动一次之后相对于移动之前逆序数的增量。这点在纸上画画就明白了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define lson l, m, rt << 1
#define rson m+1, r, rt << 1 | 1
using namespace std;
const int MAXN = 5010;
int sum[MAXN<<2];
void PushUp(int rt)
{
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void Build(int l, int r, int rt)
{
if(l == r)
return;
int m = (l+r)>>1;
Build(lson);
Build(rson);
}
void UpData(int pos, int a, int l, int r, int rt)
{
if(l == r)
{
sum[rt] += a;
return;
}
int m = (l+r)>>1;
if(pos <= m)
UpData(pos, a, lson);
else
UpData(pos, a, rson);
PushUp( rt );
}
int Query(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R)
{
return sum[rt];
}
int m = (l+r)>>1;
int ret = 0;
if(L <= m)
ret += Query(L, R, lson);
if(R > m)
ret += Query(L, R, rson);
return ret;
}
int main()
{
int x[MAXN];
int n;
while(~scanf("%d", &n))
{
memset(sum, 0, sizeof(sum));
Build(0, n-1, 1);
memset(x, 0, sizeof(x));
int num = 0;
for(int i = 0; i < n; ++i)
{
scanf("%d", &x[i]);
num += Query(x[i], n-1, 0, n-1, 1);
UpData(x[i], 1, 0, n-1, 1);
}
int ret = num;
for(int i = 0; i < n; ++i)
{
num += n-1 - x[i] - x[i];
ret = min(ret, num);
}
printf("%d\n", ret);
}
return 0;
}