hdu 1394 (线段树求逆序数)

时间:2023-03-09 08:46:33
hdu 1394  (线段树求逆序数)

<题目链接>

题意描述:

给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找出其中最小的一个输出!

解题分析:

先利用线段树求出初始序列的逆序数,这里有一个非常巧妙的地方,由于比a[i]大的数是离散且连续的,所以可以用线段树来实现(与线段树节点的区间特性符合)。

#include <bits/stdc++.h>
using namespace std; #define Lson rt<<1,l,mid
#define Rson rt<<1|1,mid+1,r
const int N = 5e3+;
int n;
int tr[N<<],arr[N];
inline void Pushup(int rt){ tr[rt]=tr[rt<<]+tr[rt<<|]; }
void update(int rt,int l,int r,int loc){
if(l==r){ tr[rt]+=;return; } //将这个点插入线段树
int mid=l+r>>;
if(loc<=mid)update(Lson,loc);
if(loc>mid)update(Rson,loc);
Pushup(rt);
}
int query(int rt,int l,int r,int L,int R){
if(L<=l && r<=R){ return tr[rt]; }
int mid=l+r>>;
int ans=;
if(L<=mid)ans+=query(Lson,L,R);
if(R>mid)ans+=query(Rson,L,R);
return ans;
}
int main(){
while(~scanf("%d",&n)){
memset(tr,,sizeof(tr));
int sum=;
for(int i=;i<=n;i++){
scanf("%d",&arr[i]);
sum+=query(,,n-,arr[i]+,n-); //逆序数之前插入的,比这个数要大的数的个数
update(,,n-,arr[i]);
}
//此时ans为原序列的逆序数
int ans=sum;
for(int i=;i<=n-;i++){ //只用重新算n-1次就行,因为第n次与原序列相同
sum+=(n--*arr[i]); //注意这里求逆序数,必须是在前一个序列的基础上求
ans=min(ans,sum);
}
printf("%d\n",ans);
}
}