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); } }