HDOJ/HDU 2838 Cow Sorting

  题意:每次交换数组相邻的两个位置的值,耗费是两个值的和。求使数组排序的最小花费;

  解法:树状数组;

  我的做法是从后往前遍历数组,对a[i],求出比a[i]小下标却比i大的元素个数x,然后求出这x个值的和temp,sum(a[i]*x+temp)(1<=i<=n)即为所求。至于为什么要这样做,原因很简单,大家都知道最少的交换次数是求逆序数。但是总花费最小是不是也是这样求?答案是:是。

  这是可以证明的,因为每次只能交换相邻的两个数,所以只要存在a[i]>a[j](i<j)则a[i],a[j]则至少会交换一次。所以当他们只交换一次的时候总花费是最小的。

  还有一个需要注意的地方是x必须是__int64。100000个数求任意一个数比他小且下标比他大的元素个数int型都不会溢出。但是如果这个值再乘以a[i]就可能会溢出。

  贴代码:

#include <iostream>
#include <string.h>
#include <cstdio>
#include <algorithm>

using namespace std;

#define LL __int64

const int N = 100005;
int tree[N];
LL  sum[N];

inline int Lowbit(int x) { return x & (-x); }

void Update(int x, int c) {
    int i, maxn = N;
    for (i = x; i < maxn; i += Lowbit(i)) {
        tree[i] += c;
        sum[i]  += x;
    }
}


LL Getsum(int x, LL &tot) {

    int i;
    LL temp = 0;
    tot = 0;

    for (i = x; i >= 1; i -= Lowbit(i)) {
        temp += tree[i];
        tot += sum[i];
    }
    return temp;
}

int a[N];

int main() {

    int n;

    while (~scanf("%d", &n)) {

        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);

        memset(tree, 0, sizeof(tree));
        memset(sum, 0, sizeof(sum));

        LL s = 0;

        for (int i = n; i >= 1; i--) {
            LL temp;
            LL x = Getsum(a[i], temp);
            //如果x不是__int64那么x*a[i]可能会溢出,因为两个都不是__int64
            s += (a[i] * x + temp);
            Update(a[i], 1);
        }

        printf("%I64d\n", s);
    }
}
posted @ 2011-09-25 10:34  like@neu  阅读(235)  评论(0编辑  收藏  举报