HDU1394 Minimum Inversion Number 线段树

  题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394

  题目大意:定义逆序数, 一个数列前面的数比后面的数大的数对的个数, 给出数列, 求逆序数。

  解题思路:我现在学习的是线段树, 所以我用线段树的思想去解决问题。暴力是n^2的复杂度, 但是只要知道了第一个数列的逆序数, 其他的都可以通过用O(1) 的方式得到, 所以关键是求初始序列的逆序数, 暴力方法是n^2, 由于最大不超过n所以问题就转化为了插入一个a[i], 求前i-1个数中a[i]~n的数的个数。这就是线段树的一个查询功能。 问题就转化为O(nlogn)了。

  代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

#define lson l, m, rt << 1
#define rson m+1, r, rt << 1 | 1
const int maxn = 5555;
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 ) return;
    int m = (l+r) >> 1;
    build(lson);
    build(rson);
}

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 = (r+l) >> 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) == 1 ) {
        build(0, n-1, 1);
        int sum = 0;
        for( int i = 0; i < n; i++ ) {
            scanf( "%d", &x[i]);
            sum += query(x[i], n-1, 0, n-1, 1);
            update(x[i], 0, n-1, 1);
        }
        int ret = sum;
        for( int i = 0; i < n; i++ ) {
            sum += n - x[i] - x[i] - 1;
            ret = min( ret, sum );
        }
        printf( "%d\n", ret );
    }
    return 0;
}
View Code

  思考:  我接触线段树的时间实在是很短, 所以很多问题联系不起来。线段树为什么要叫线段树呢, 因为他处理的就是区间问题, 所以如果将问题转化为区间查询问题, 就可以用线段树解决问题,当然还有更新节点等一些很麻烦的事情, 所以说还得慢慢做题积累, 一个月后我感觉线段树差不多会入门了。

posted on 2017-06-06 09:56  FriskyPuppy  阅读(181)  评论(0编辑  收藏  举报

导航