nyoj 117 逆序数

    用树状数组求逆序数。基本思路就是先离散化,就是让数值都不那么大,把数值弄紧凑,即9 1 5 8 3变成5 1 3 4 2.过程中需要注意下标与数值的交替使用。
    然后就是用树状数组。本来逆序数的求法就是求出在每个数前面的,且数值大于它的数的个数,相加。用树状数组方便求和。开始时,先把c数组全置为0,来一个aa[i],就把c[i]赋值。在求逆序数的时候,就是一边放一边求。即放一个,求出比这个数小的,而且已经被放置了的(就是排列时次序在它前面的)数的总数和。
    用树状数组的好处就是每放一个数,就可以从下往上,把这个数以上的包含这这个数的数据段都更新。然后求和的时候也是,从上往下,一段数据一段数据地加。

#include   algorithm
#include   iostream 
#include   stdio.h 
#include   string.h 
using namespace std;

typedef struct
{
    int v, order;
}node;
node in[1000005];
int n, c[1000005], aa[1000005];         //aa是离散化后的数组,c是线段数组,即每个ci是几个数据之和。
int cmp(node a, node b)
{
    if(a.v == b.v)
        return a.order < b.order   ;               //这里很重要!因为sort排序不稳定。要求逆序,必须保证每个数的位次。
    return a.v < b.v;
}
int lowbit(int x)
{
    return x&(-x);
}

void update(int pos)
{
    while(pos <= n)
    {
        c[pos] = c[pos] + 1;
        pos += lowbit(pos);
    }
}

int sum(int pos)
{
    int s = 0;
    while(pos > 0)
    {
        s += c[pos];
        pos -= lowbit(pos);
    }
    return s;
}

int main()
{
    int t, i;
    long long ans;
    scanf("%d", &t);
    while(t--)
    {
        ans = 0;
        scanf("%d", &n);
        for(i = 1 ; i <= n ; i++)
        {
            scanf("%d", &in[i].v);
            in[i].order = i;                           //次序等于输入的次序。
        }
        sort(in, in+n+1, cmp);           //将他们按照数值大小排序。
        for(i = 1 ; i <= n ; i++)
              aa[in[i].order] = i;         //   然后恢复次序。第一个数就是数值最小的,第二个是数值次小的……所以数值最小的新排序就是1,数值次小的新排序就是2……
        memset(c, 0, sizeof(c));
        for(i = 1 ; i <= n ; i++)
        {
            update(aa[i]);
            ans += i - sum(aa[i]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值