hdu 1394 线段树计算逆序数

线段树计算逆序数的原理:

用线段树来统计已插入的数的个数(所以要保证最大的那个数不能太大,否则数组都开不了),然后每插入一个数,就查询比插入的数大的个数,累加即可。

这个题还有一个特点就是,题目给的是0至n-1的全排列,也就是说每个数都不同。那么abcde的逆序数与bcdea的逆序数就很明了了。

假设比a小的数有t个,那么比a大的数有n-t-1个,那么abcde转换至bcdea的逆序数就增加了n-t-1,减少了t

不需要build函数建树,因为初始状态没有数插入,直接menset就可以了

#include <bits/stdc++.h>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;

const int MAXN = 5008;
int cnt[MAXN<<2];

int query(int L, int R, int l, int r, int rt)
{
    if(L <= l && r <= R) return cnt[rt];
    int ret = 0;
    int m = (l + r) >> 1;
    if(L <= m) ret += query(L, R, lson);
    if(R > m) ret += query(L, R, rson);
    return ret;
}

void updata(int p, int l, int r, int rt)
{
    if(l == r)
    {
        cnt[rt]++;
        return;
    }
    int m = (l + r) >> 1;
    if(p <= m) updata(p, lson);
    else updata(p, rson);
    cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1];
}

int a[MAXN];

int main()
{
//    freopen("in.txt", "r", stdin);
    int n;
    while(~scanf("%d", &n))
    {
        memset(cnt, 0, sizeof(cnt));
        int sum = 0;
        for(int i=0; i<n; i++)
        {
            scanf("%d", &a[i]);
            sum += query(a[i]+1, n-1, 1, n, 1);
            updata(a[i], 1, n, 1);
        }
        int ans = sum;
        for(int i=0; i<n; i++)
        {
            sum += n - 2*a[i] - 1;
            ans = min(ans, sum);
        }
        printf("%d\n", ans);
    }
    return 0;
}

 

posted @ 2017-08-12 09:28  Pacify  阅读(143)  评论(0编辑  收藏  举报