题意:
给出一个由0到n-1组成的序列,每次把队首的元素移到队尾,求形成的n个序列中逆序对数目的最小值(n<=5000)
题解:
首先可以对初始数组求一次逆序对(O(nlogn))
很自然的想到,如果每次变幻数组后再求逆序对(O(n²logn)),显然会爆
但我们可以做到O(1)的递推
假设原始数组的逆序对个数是sum个,如果把x[1]放到x[n]后面,逆序列个数会减少x[1]个,相应会增加n-(x[1]+1)个
即有
sum+=(n-(x[i]+1))-(x[i]);
意识到这个就很好做了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define getmid int m=(l+r)>>1
const int maxn=5555;
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)
{
sum[rt]=0;
if(l==r) return;
getmid;
Build(lson);
Build(rson);
}
void Update(int p,int l,int r,int rt)
{
if(l==r)
{
sum[rt]++;return;
}
getmid;
if(p<=m) Update(p,lson);
else Update(p,rson);
Pushup(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r) return sum[rt];
getmid;
int ret=0;
if(L<=m) ret+=Query(L,R,lson);
if(R>m) ret+=Query(L,R,rson);
return ret;
}
int x[maxn];
int main()
{
int n;
while(~scanf("%d",&n))
{
Build(0,n-1,1);
int sum=0;
for(int 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 ret=sum;
for(int i=0;i<n;i++)
{
sum+=n-x[i]-1-x[i];
ret=min(ret,sum);
}
printf("%d\n",ret);
}
return 0;
}