既然是区间操作 我们想到了线段树
我们先建立一个空树 然后呢 按照这个序列的顺序将每个值插入 ,每次插入伴随一次查询 查询前面插入的比这次插入的大的数字的个数
然后就可以了 复杂度 nlogn 可以接受
题意:就是给出一串数,当依次在将第一个数变为最后一个数的过程中,要你求它的最小逆序数。
思路:可以用树状数组和线段数做。这里我是用线段树做的。建的是一棵空树,然后每插入一个点之前,统计大于这个数的有多少个,直到所有的数都插入完成,就结果了逆序树的统计。
要得出答案主要是利用了一个结论,如果是0到n的排列,那么如果把第一个数放到最后,对于这个数列,逆序数是减少a[i],而增加n-1-a[i]的.当然,你也可以是统计小于这个数的有多少个,然后再用已经插入树中i个元素减去小于这个数的个数,得出的结果也是在一样的。
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <queue>
#include <stack>
#define inf 0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const long long mod = 1e9 + 7;
const int maxn = 55555;
using namespace std;
typedef long long ll;
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)
{
//scanf("%d",&sum[rt]);
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
// pushup(rt);
}
void update(int p,int l,int r,int rt)
{
if(l==r)
{
sum[rt]++;
return ;
}
int m=(l+r)>>1;
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];
}
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 x[maxn];
int main()
{
int n;
while (~scanf("%d",&n))
{
build(1, n , 1);
int sum = 0;
for (int i = 1 ; i <= n ; i ++)
{
scanf("%d",&x[i]);
sum += query(x[i], n , 1, n , 1);
update(x[i], 1, n, 1);
}
int ret = sum;
for (int i = 1 ; i <= n ; i ++)
{
sum += n - x[i] - x[i] - 1;
ret = min(ret, sum);
}
printf("%d\n",ret);
}
return 0;
}