思路:之前用的树状数组做,现在尝试用线段树来做,
维护一棵线段树
,
树中的每个叶子节点表示的是这个节点是否出现过
,
比如叶子节点
i
控制的范围是
[2,2],
那么
sum[i]=1
表示
2
这个数已经出现了
,
如果
sum[i]=0
表
2
这个数还没出现
.
如果
j
节点控制范围
[4,8],
那么
sum[j]=3
表示
[4,8]
区间有
3
个数出现了
.
如果当前处理的是a[i],那么用query(a[i]+1,n,1,1,n)找到在a[i]之前出现的比a[i]值大的数有多少个,那么这个值就是a[i]的逆序数.总的逆序数ans=所有a[i]的逆序数之和.
然后经过n-1次操作的公式之前已经推出来,不多讲可以找之前的看
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 5000+5;
const int INF = 1e9;
//线段树需要维护的信息
int sum[maxn*4];
int a[maxn];
#define lson i*2,l,m
#define rson i*2+1,m+1,r
//i结点收集子结点的统计结果
void PushUp(int i)
{
sum[i]=sum[i*2]+sum[2*i+1];
}
//递归建立线段树
void build(int i,int l,int r)
{
if (l==r)
{
sum[i]=0;
return;
}
int m = (l+r)/2;
build(lson);
build(rson);
PushUp(i); //收集子结点的结果
}
//在当前区间[l,r]内查询区间[ql,qr]间的目标值
//且能执行这个函数的前提是:[l,r]与[ql,qr]的交集非空
//本函数返回的结果也就是它们交集的目标值
int query(int ql,int qr,int i,int l,int r)
{
//目的区间包含当前区间
if (ql <=l && qr>=r)
return sum[i];
int m = (l+r)/2;
int ans = 0;
if (ql <= m)
ans+=query(ql,qr,lson);
if (m < qr)
ans+=query(ql,qr,rson);
return ans;
}
//如果本题是单点更新,可以在区间[l,r]内使得第id数的值+val
//如果本题是区间更新,可以updata的参数需要将id改为ql,qr
void update(int id,int i,int l,int r)
{
if (l==r)
{
sum[i]++;
return;
}
int m = (l+r)/2;
if (id <= m)
update(id,lson);
else
update(id,rson);
PushUp(i); //时刻记得维护i结点统计信息的正确性
}
int main()
{
int cas = 1;
int n;
while (scanf("%d",&n)!=EOF && n)
{
int ans = 0;
build(1,1,n);
for (int i = 1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]++;
ans+=query(a[i]+1,n,1,1,n); //在a[i]+1到n这个区间找有没有
update(a[i],1,1,n);
}
int mins = ans;
for (int i = 1;i<n;i++)
{
ans = ans+n+1-2*a[i];
mins = min(mins,ans);
}
printf("%d\n",mins);
}
}