首先看看这道题什么意思:
输入
3
3 2 1
输出
9
其实就是冒泡排序,排成正序。
冒泡排序实际上就是在找逆序对。
即在满足 i<j(下标)的对情况下,有多少个ai>aj。如果大于就交换。
对于求逆序对,我们有一个结论:
我们如果确定一个数x,如果这个数前面有k1个数比它大,后面有k2个数比它小,那么就需要交换k1+k2次。
证明
前面有k1个数比它大,那么至少把k1个数全部交换到它后面,后面有k2个数比它小,那么至少就要这些小的数全部交换到x前面。所以就需要交换k1+k2次。
验证
比如4前面有1个数比4大,4后面有2个数比4大,那么总共就需要交换3次。
交换过程:
总结
那么这道题就演变为了求: 有多少个比x大的数在x前面 + 有多少个比x小的数在x后面。
这道题用树状数组
code
这道题可能会爆int,因为n最大为,那么假如要交换次,那么不高兴程度和就为: ,用高斯求和:,而int的最大范围是,因此我们需要开个long long。
代码部分我们分为两步求,第一步求 每一个数前面有多少个数比它大,比如我们要求有多少个数比hi大,那么是不是应该是 hi+1,hi+2,……10^6,我们可以用求和公式:s[n]-s[hi]求出。
这里树状数组不是求区间和,而是求落在区间内的个数
/*
举例:1+2+3+4+5+6+7+8+9+10=55
推导出:1-n之间的加法公式:n*(n-1)
*/
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1000010;
int n;
int h[N], tr[N];
int sum[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int v)
{
for (int i = x; i < N; i += lowbit(i)) tr[i] += v;
}
int query(int x) //O(logN)查找逆序对儿的数量
{
int res = 0;
for (int i = x; i > 0; i -= lowbit(i)) res += tr[i];
return res;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> h[i];
h[i]++;
}
//每个数前面有多少个数比它大
for (int i = 1; i <= n; i++) { //正序处理
sum[i] = query(N - 1) - query(h[i]);
add(h[i], 1);
}
//树状数组清空
memset(tr, 0, sizeof tr);
//每个数后面有多少个数比它大
for (int i = n; i > 0; i--) {
sum[i] += query(h[i] - 1);
add(h[i], 1);
}
long long res = 0;
for (int i = 1; i <= n; i++)
res += (long long)sum[i] * (sum[i] + 1) / 2;
cout << res << endl;
}