问题描述
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
例子:
[1,3,4,2,5]
1左边比1小的数,没有;
3左边比3小的数,1;
4左边比4小的数,1、3;
2左边比2小的数,1;
5左边比5小的数,1、3、4、2;
所以小和为1+1+3+1+1+3+4+2=16
对于此问题,我们可以逆向求解一下
1左边比1小的数 => 1右边有几个比1大的数(4个)1 * 4
3左边比3小的数 => 3右边有几个比3大的数(2个)3 * 2
4左边比4小的数 => 4右边有几个比4大的数(1个)4 * 1
2左边比2小的数 => 2右边有几个比2大的数(1个)2 * 1
5左边比5小的数 => 5右边有几个比5大的数(没有)
所以小和为 1 * 4 + 3 * 2 + 4 * 1 + 2 * 1 = 16
在归并排序的基础上,加上一行代码即可求解此题
sum += nums[p1] * (right - p2 + 1);
还有一点不一样的就是排序的时候,左右相等的情况下,要先放右边的数。
在每一次归并操作比较元素大小的时候,加一步求和操作
int small_sum(vector<int>& nums, int left, int mid, int right)
{
vector<int> num;
int p1 = left;
int p2 = mid + 1;
int sum = 0;
while (p1 <= mid && p2 <= right)
{
if (nums[p1] < nums[p2])
{
sum += nums[p1] * (right - p2 + 1);
num.push_back(nums[p1++]);
}
else
{
num.push_back(nums[p2++]);
}
}
while (p1 <= mid)
{
num.push_back(nums[p1++]);
}
while (p2 <= right)
{
num.push_back(nums[p2++]);
}
for (int i = 0; i < num.size(); i++)
{
nums[left + i] = num[i];
}
return sum;
}
int small_sum_merge_sort(vector<int>& nums, int left, int right)
{
if (left == right)
{
return 0;
}
int mid = left + ((right - left) >> 1);
// small_sum_merge_sort(nums, left, mid);
// small_sum_merge_sort(nums, mid + 1, right);
// small_sum(nums, left, mid, right);
return small_sum_merge_sort(nums, left, mid) + small_sum_merge_sort(nums, mid + 1, right) + small_sum(
nums, left, mid, right);
}