逆序数 线段树

既然是区间操作 我们想到了线段树

我们先建立一个空树 然后呢 按照这个序列的顺序将每个值插入 ,每次插入伴随一次查询 查询前面插入的比这次插入的大的数字的个数

然后就可以了 复杂度 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;
}

猜你喜欢

转载自blog.csdn.net/abc1235454/article/details/89103436
今日推荐