import java.util.*; /** * NC349 计算数组的小和 * @author d3y1 */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param nums int整型ArrayList * @return long长整型 */ public long calArray (ArrayList<Integer> nums) { // return solution1(nums); return solution2(nums); // return solution3(nums); } /** * 二分: 超时! * @param nums * @return */ private long solution1(ArrayList<Integer> nums){ long result = 0; int n = nums.size(); ArrayList<Integer> list = new ArrayList<>(); list.add(nums.get(0)); // pos: 当前num(target)插入list位置(索引) int left,right,mid,last,target,pos,sum; for(int i=1; i<n; i++){ last = list.get(i-1); target = nums.get(i); sum = 0; // 直接附加 if(last <= target){ pos = i; list.add(pos, target); } // 二分查找 else{ left = 0; right = i-1; while(left <= right){ mid = (left+right)>>1; if(list.get(mid) <= target){ left = mid+1; }else if(list.get(mid) > target){ right = mid-1; } } pos = left; list.add(pos, target); } // 求和(小于等于当前num(target)) for(int j=0; j<pos; j++){ sum += list.get(j); } result += sum; } return result; } ///////////////////////////////////////////////////////////////////////////////////////////////////// /** * 树状数组(Binary Indexed Tree)(也称Fenwick树): 动态维护前缀和 * * 参考: https://www.bilibili.com/video/BV1pE41197Qj/?vd_source=fa6990445a0e68c4b18e85e07647ab23 * * @param nums * @return */ private long solution2(ArrayList<Integer> nums){ long result = 0; int n = nums.size(); // 离散化处理 discretization(nums); // 初始化 init(nums.size()+1); int num; for(int i=0; i<n; i++){ num = nums.get(i); // 树状数组id int id = getId(num); // 根据id获取前缀和 result += query(id); // 更新前缀和 update(id, num); } return result; } // 树状数组t private int[] t; // 将nums离散化, 去重+排序, 转化为数组a private int[] a; /** * 初始化 * @param length */ private void init(int length) { t = new int[length]; Arrays.fill(t, 0); } /** * 非负整数n在二进制表示下最低位1及后面的0所构成的数值 * * 示例: * lowBit(44) = lowBit(101100[2进制]) = 100[2进制] = 4[10进制] * * n 101100 * 取反: ~n 010011 * 取反+1: ~n+1 010100 * * ~n+1 = -n (计算机存储使用补码, 取反加1后的值即为负的这个数) * * lowBit(44) = n & (~n+1) = n & (-n) * * @param x * @return */ private int lowBit(int x) { return x & (-x); } /** * 更新前缀和: 从当前节点往树根逐层更新父节点 * @param pos * @param num */ private void update(int pos, int num) { while (pos < t.length) { t[pos] += num; // 当前节点的父节点 pos += lowBit(pos); } } /** * 查询前缀和: 从当前节点往左上逐个累加节点值 * @param pos * @return */ private long query(int pos) { long ret = 0; while (pos > 0) { ret += t[pos]; // 当前节点的左上节点 pos -= lowBit(pos); } return ret; } /** * 离散化处理 * * nums数组中可能有负数或者可能稀疏 -> 离散化处理 * * @param nums */ private void discretization(ArrayList<Integer> nums) { Set<Integer> set = new HashSet<>(nums); int size = set.size(); a = new int[size]; int index = 0; for (int num : set) { a[index++] = num; } Arrays.sort(a); } /** * 根据num获取树状数组id: num -> id * @param num * @return */ private int getId(int num) { return Arrays.binarySearch(a, num)+1; } ///////////////////////////////////////////////////////////////////////////////////////////////////// /** * 归并 * * nums[i]最终累加次数 等价于 后面有多少个大于等于nums[i]的数(count[i]) * * 示例: * i 0 1 2 3 4 5 * nums 1 3 5 2 4 6 * count 5 3 1 2 1 0 * * result = 1*5 + 3*3 + 5*1 + 2*2 + 4*1 * = 5 + 9 + 5 + 4 + 4 * = 27 * * @param nums * @return */ private long solution3(ArrayList<Integer> nums){ Integer[] numsArr = new Integer[nums.size()]; nums.toArray(numsArr); return mergeSort(numsArr, 0, nums.size()-1); } /** * 归并排序 * @param nums * @param left * @param right * @return */ private long mergeSort(Integer[] nums, int left, int right) { if(left >= right) { return 0; } int mid = left+((right-left)>>1); return mergeSort(nums, left, mid)+mergeSort(nums, mid+1, right)+merge(nums, left, mid, right); } /** * 合并 * @param nums * @param left * @param mid * @param right * @return */ private long merge(Integer[] nums, int left, int mid, int right) { long result = 0L; int[] help = new int[right-left+1]; int i = 0; int p = left; int q = mid+1; while(p<=mid && q<=right) { result += (nums[p]<=nums[q]) ? nums[p]*(right-q+1) : 0; help[i++] = (nums[p]<=nums[q]) ? nums[p++] : nums[q++]; } while(p <= mid) { help[i++] = nums[p++]; } while(q <= right) { help[i++] = nums[q++]; } for(i=0; i<help.length; i++) { nums[left+i] = help[i]; } return result; } }