import java.util.*;
/**
* NC349 计算数组的小和
* @author d3y1
*/
public class Solution {
// 树状数组t
private int[] t;
// 线段树
private int[] segmentTree;
// 离散化: 将nums离散化(去重+排序), 转化为数组d
private int[] d;
private int n;
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 相似 -> NC360 右侧更小数 [nowcoder]
*
* 注意: 线段树lazy(懒惰标记)适用于区间修改(改善区间修改效率) -> https://oi-wiki.org/ds/seg/#线段树的区间修改与懒惰标记
*
* @param nums int整型ArrayList
* @return long长整型
*/
public long calArray (ArrayList<Integer> nums) {
// return solution1(nums);
// return solution2(nums);
// return solution3(nums);
// return solution4(nums);
// return solution44(nums);
// return solution444(nums);
// return solution4444(nums);
// return solution5(nums);
return solution55(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();
// 离散化处理
discrete(nums);
// 初始化
init(nums.size()+1);
int num;
for(int i=0; i<n; i++){
num = nums.get(i);
// 树状数组id
int id = getIdByNum(num);
// 根据id获取前缀和
result += query(id);
// 更新前缀和
update(id, num);
}
return result;
}
/**
* 初始化
* @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[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;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// /**
// * 线段树(Segment Tree): 数组实现(堆式存储)
// * @param nums
// * @return
// */
// private long solution4(ArrayList<Integer> nums){
// n = nums.size();
// segmentTree = new int[n*4];
// long result = 0L;
// // 离散化处理
// discrete(nums);
// int num;
// // 从后往前遍历
// for(int i=0; i<n; i++){
// num = nums.get(i);
// // 线段树id
// int id = getIdByNum(num);
// // 根据id获取区间和 小于等于(id) range: left<=right
// result += sumRange(0, id);
// // 更新前缀和
// updateVal(id, num);
// }
// return result;
// }
// /**
// * 更新: 根据id进行更新
// * @param id
// * @param val
// */
// public void updateVal(int id, int val) {
// change(id, val, 0, 0, n);
// }
// /**
// * 区间和
// * @param left
// * @param right
// * @return
// */
// public int sumRange(int left, int right) {
// return range(left, right, 0, 0, n);
// }
// /**
// * 递归: 更新线段树
// * @param index
// * @param val
// * @param node
// * @param start
// * @param end
// */
// private void change(int index, int val, int node, int start, int end) {
// if(start == end){
// segmentTree[node] += val;
// return;
// }
// int mid = start+(end-start)/2;
// if(index <= mid){
// change(index, val, node*2+1, start, mid);
// }else{
// change(index, val, node*2+2, mid+1, end);
// }
// segmentTree[node] = segmentTree[node*2+1]+segmentTree[node*2+2];
// }
// /**
// * 递归: 区间和
// * @param left
// * @param right
// * @param node
// * @param start
// * @param end
// * @return
// */
// private int range(int left, int right, int node, int start, int end) {
// if(left==start && right==end){
// return segmentTree[node];
// }
// int mid = start+(end-start)/2;
// if(right <= mid){
// return range(left, right, node*2+1, start, mid);
// }else if(left > mid){
// return range(left, right, node*2+2, mid+1, end);
// }else{
// return range(left, mid, node*2+1, start, mid) + range(mid+1, right, node*2+2, mid+1, end);
// }
// }
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 线段树(Segment Tree): 数组实现(堆式存储)
* @param nums
* @return
*/
private long solution44(ArrayList<Integer> nums){
n = nums.size();
segmentTree = new int[n*4+1];
long result = 0L;
// 离散化处理
discrete(nums);
int num;
// 从后往前遍历
for(int i=0; i<n; i++){
num = nums.get(i);
// 线段树id
int id = getIdByNum(num);
// 根据id获取区间和 小于等于(id) range: left<=right
result += rangeSum(0, id);
// 更新前缀和
modify(id, num);
}
return result;
}
/**
* 更新: 根据id进行更新(单点修改)
* @param id
* @param val
*/
public void modify(int id, int val) {
modify(id, id, val, 0, 0, n);
}
/**
* 递归: 更新线段树(区间修改)
* @param left
* @param right
* @param val
* @param root
* @param start
* @param end
*/
private void modify(int left, int right, int val, int root, int start, int end){
// [left, right]: 修改区间
// [start, end]: 当前节点区间
if(left<=start && end<=right){
segmentTree[root] += (end-start+1)*val;
return;
}
int mid = start+(end-start)/2;
if(left <= mid){
modify(left, right, val, root*2+1, start, mid);
}
if(right > mid){
modify(left, right, val, root*2+2, mid+1, end);
}
segmentTree[root] = segmentTree[root*2+1]+segmentTree[root*2+2];
}
/**
* 区间和(区间查询)
* @param left
* @param right
* @return
*/
public int rangeSum(int left, int right) {
return rangeSum(left, right, 0, 0, n);
}
/**
* 递归: 区间和
* @param left
* @param right
* @param root
* @param start
* @param end
* @return
*/
private int rangeSum(int left, int right, int root, int start, int end){
// [left, right]: 查询区间
// [start, end]: 当前节点区间
if(left<=start && end<=right){
return segmentTree[root];
}
int mid = start+(end-start)/2;
int sum = 0;
if(left <= mid){
sum += rangeSum(left, right, root*2+1, start, mid);
}
if(right > mid){
sum += rangeSum(left, right, root*2+2, mid+1, end);
}
return sum;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 线段树(Segment Tree): 数组实现(堆式存储)
* @param nums
* @return
*/
private long solution444(ArrayList<Integer> nums){
n = nums.size();
segmentTree = new int[n*4+1];
long result = 0L;
// 离散化处理
discrete(nums);
int num;
// 从后往前遍历
for(int i=0; i<n; i++){
num = nums.get(i);
// 线段树id
int id = getIdByNum(num);
// 根据id获取区间和 小于等于(id) range: left<=right
result += querySum(0, id);
// 更新前缀和
change(id, num);
}
return result;
}
/**
* 更新: 根据id进行更新(单点修改)
* @param id
* @param val
*/
public void change(int id, int val) {
change(id, id, val, 1, 0, n);
}
/**
* 递归: 更新线段树(区间修改)
* @param left
* @param right
* @param val
* @param root
* @param start
* @param end
*/
private void change(int left, int right, int val, int root, int start, int end){
// [left, right]: 修改区间
// [start, end]: 当前节点区间
if(left<=start && end<=right){
segmentTree[root] += (end-start+1)*val;
return;
}
int mid = start+(end-start)/2;
if(left <= mid){
change(left, right, val, root*2, start, mid);
}
if(right > mid){
change(left, right, val, root*2+1, mid+1, end);
}
segmentTree[root] = segmentTree[root*2]+segmentTree[root*2+1];
}
/**
* 区间和(区间查询)
* @param left
* @param right
* @return
*/
public int querySum(int left, int right) {
return querySum(left, right, 1, 0, n);
}
/**
* 递归: 区间和
* @param left
* @param right
* @param root
* @param start
* @param end
* @return
*/
private int querySum(int left, int right, int root, int start, int end){
// [left, right]: 查询区间
// [start, end]: 当前节点区间
if(left<=start && end<=right){
return segmentTree[root];
}
int mid = start+(end-start)/2;
int sum = 0;
if(left <= mid){
sum += querySum(left, right, root*2, start, mid);
}
if(right > mid){
sum += querySum(left, right, root*2+1, mid+1, end);
}
return sum;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 线段树(Segment Tree): 数组实现(堆式存储)
* @param nums
* @return
*/
private long solution4444(ArrayList<Integer> nums){
n = nums.size();
segTree = new SegNode[n*4+1];
segTree[1] = new SegNode(0, n);
long result = 0L;
// 离散化处理
discrete(nums);
int num;
// 从后往前遍历
for(int i=0; i<n; i++){
num = nums.get(i);
// 线段树id
int id = getIdByNum(num);
// 根据id获取区间和 小于等于(id) range: left<=right
result += getSum(0, id);
// 更新前缀和
alter(id, num);
}
return result;
}
// 线段树
private SegNode[] segTree;
/**
* 更新: 根据id进行更新(单点修改)
* @param id
* @param val
*/
public void alter(int id, int val) {
alter(id, id, val, 1);
}
/**
* 递归: 更新线段树(区间修改)
* @param left
* @param right
* @param val
* @param root
*/
private void alter(int left, int right, int val, int root){
// [left, right]: 修改区间
// [start, end]: 当前节点区间
if(left<=segTree[root].start && segTree[root].end<=right){
segTree[root].sum += (segTree[root].end-segTree[root].start+1)*val;
return;
}
int mid = segTree[root].start+(segTree[root].end-segTree[root].start)/2;
if(segTree[root*2] == null){
segTree[root*2] = new SegNode(segTree[root].start, mid);
}
if(segTree[root*2+1] == null){
segTree[root*2+1] = new SegNode(mid+1, segTree[root].end);
}
if(left <= mid){
alter(left, right, val, root*2);
}
if(right > mid){
alter(left, right, val, root*2+1);
}
segTree[root].sum = segTree[root*2].sum+segTree[root*2+1].sum;
}
/**
* 区间和(区间查询)
* @param left
* @param right
* @return
*/
public int getSum(int left, int right) {
return getSum(left, right, 1);
}
/**
* 递归: 区间和
* @param left
* @param right
* @param root
* @return
*/
private int getSum(int left, int right, int root){
// [left, right]: 查询区间
// [start, end]: 当前节点区间
if(left<=segTree[root].start && segTree[root].end<=right){
return segTree[root].sum;
}
int mid = segTree[root].start+(segTree[root].end-segTree[root].start)/2;
if(segTree[root*2] == null){
segTree[root*2] = new SegNode(segTree[root].start, mid);
}
if(segTree[root*2+1] == null){
segTree[root*2+1] = new SegNode(mid+1, segTree[root].end);
}
int sum = 0;
if(left <= mid){
sum += getSum(left, right, root*2);
}
if(right > mid){
sum += getSum(left, right, root*2+1);
}
return sum;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// /**
// * 线段树(Segment Tree): 树实现
// * @param nums
// * @return
// */
// private long solution5(ArrayList<Integer> nums){
// n = nums.size();
// TreeNode root = new TreeNode(0, n);
// long result = 0L;
// // 离散化处理
// discrete(nums);
// int num;
// // 从后往前遍历
// for(int i=0; i<n; i++){
// num = nums.get(i);
// // 线段树id
// int id = getIdByNum(num);
// // 根据id获取区间和 小于等于(id) range: left<=right
// result += sumRange(root, 0, id);
// // 更新前缀和
// update(root, id, num);
// }
// return result;
// }
// /**
// * 更新: 根据id进行更新
// * @param root
// * @param id
// * @param val
// */
// public void update(TreeNode root, int id, int val) {
// change(root, id, val);
// }
// /**
// * 递归: 更新线段树
// * @param root
// * @param index
// * @param val
// */
// private void change(TreeNode root, int index, int val) {
// if(root.start == root.end){
// root.sum += val;
// return;
// }
// int mid = root.start+(root.end-root.start)/2;
// if(root.left == null){
// root.left = new TreeNode(root.start, mid);
// }
// if(root.right == null){
// root.right = new TreeNode(mid+1, root.end);
// }
// if(index <= mid){
// change(root.left, index, val);
// }else{
// change(root.right, index, val);
// }
// root.sum = root.left.sum+root.right.sum;
// }
// /**
// * 区间和
// * @param root
// * @param left
// * @param right
// * @return
// */
// public int sumRange(TreeNode root, int left, int right) {
// return range(root, left, right);
// }
// /**
// * 递归: 区间和
// * @param root
// * @param left
// * @param right
// * @return
// */
// private int range(TreeNode root, int left, int right) {
// if(root == null){
// return 0;
// }
// if(left==root.start && right==root.end){
// return root.sum;
// }
// int mid = root.start+(root.end-root.start)/2;
// if(root.left == null){
// root.left = new TreeNode(root.start, mid);
// }
// if(root.right == null){
// root.right = new TreeNode(mid+1, root.end);
// }
// if(right <= mid){
// return range(root.left, left, right);
// }else if(left > mid){
// return range(root.right, left, right);
// }else{
// return range(root.left, left, mid)+range(root.right, mid+1, right);
// }
// }
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 线段树(Segment Tree): 树实现
* @param nums
* @return
*/
private long solution55(ArrayList<Integer> nums){
n = nums.size();
TreeNode root = new TreeNode(0, n);
long result = 0L;
// 离散化处理
discrete(nums);
int num;
// 从后往前遍历
for(int i=0; i<n; i++){
num = nums.get(i);
// 线段树id
int id = getIdByNum(num);
// 根据id获取区间和 小于等于(id) range: left<=right
result += rangeSum(root, 0, id);
// 更新前缀和
modify(root, id, num);
}
return result;
}
/**
* 更新: 根据id进行更新(单点修改)
* @param root
* @param id
* @param val
*/
public void modify(TreeNode root, int id, int val) {
modify(root, id, id, val);
}
/**
* 递归: 更新线段树(区间修改)
* @param root
* @param left
* @param right
* @param val
*/
private void modify(TreeNode root, int left, int right, int val) {
if(left<=root.start && root.end<=right){
root.sum += (root.end-root.start+1)*val;
return;
}
int mid = root.start+(root.end-root.start)/2;
if(root.left == null){
root.left = new TreeNode(root.start, mid);
}
if(root.right == null){
root.right = new TreeNode(mid+1, root.end);
}
if(left <= mid){
modify(root.left, left, right, val);
}
if(right > mid){
modify(root.right, left, right, val);
}
root.sum = root.left.sum+root.right.sum;
}
/**
* 区间和(区间查询)
* @param root
* @param left
* @param right
* @return
*/
public int rangeSum(TreeNode root, int left, int right) {
if(root == null){
return 0;
}
if(left<=root.start && root.end<=right){
return root.sum;
}
int mid = root.start+(root.end-root.start)/2;
if(root.left == null){
root.left = new TreeNode(root.start, mid);
}
if(root.right == null){
root.right = new TreeNode(mid+1, root.end);
}
int sum = 0;
if(left <= mid){
sum += rangeSum(root.left, left, right);
}
if(right > mid){
sum += rangeSum(root.right, left, right);
}
return sum;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 离散化处理
*
* nums数组中可能有负数或者可能稀疏 -> 离散化处理
*
* @param nums
*/
private void discrete(ArrayList<Integer> nums) {
Set<Integer> set = new HashSet<>(nums);
int size = set.size();
d = new int[size];
int index = 0;
for(int num : set){
d[index++] = num;
}
Arrays.sort(d);
}
/**
* 二分: 根据num获取树状数组id (num -> id)
* @param num
* @return
*/
private int getIdByNum(int num) {
return Arrays.binarySearch(d, num)+1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
private class TreeNode {
// 区间
int start,end;
int sum = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int start, int end) {
this.start = start;
this.end = end;
}
}
private class SegNode {
private int start;
private int end;
private int sum = 0;
public SegNode(int start, int end){
this.start = start;
this.end = end;
}
}
}