import java.util.*;

/**
 * NC360 右侧更小数
 * @author d3y1
 */
public class Solution {
    private int[] index;
    private int[] result;

    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 相同 -> 315    计算右侧小于当前元素的个数   [leetcode]
     * 相似 -> NC349  计算数组的小和             [nowcoder]
     *
     * @param nums int整型ArrayList
     * @return int整型ArrayList
     */
    public ArrayList<Integer> smallerCount (ArrayList<Integer> nums) {
        // return solution1(nums);
        return solution2(nums);
        // return solution3(nums);
        // return solution4(nums);
        // return solution5(nums);
    }

    /**
     * 二分
     * @param nums
     * @return
     */
    private ArrayList<Integer> solution1(ArrayList<Integer> nums){
        int n = nums.size();
        this.result = new int[n];

        ArrayList<Integer> list = new ArrayList<>();
        list.add(nums.get(n-1));

        // pos: 当前num(target)插入list位置(索引)
        int left,right,mid,last,target,pos,sum;
        for(int i=n-2; i>=0; i--){
            last = list.get(n-i-2);
            target = nums.get(i);
            // 直接附加
            if(last < target){
                pos = n-i-1;
                list.add(pos, target);
            }
            // 二分查找
            else{
                left = 0;
                right = n-i-2;
                while(left <= right){
                    mid = (left+right)>>1;
                    if(list.get(mid) < target){
                        left = mid+1;
                    }else{
                        right = mid-1;
                    }
                }

                pos = left;
                list.add(pos, target);
            }
            result[i] = pos;
        }

        ArrayList<Integer> arrayList = new ArrayList<>();
        for(int cnt : result){
            arrayList.add(cnt);
        }

        return arrayList;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 树状数组(Binary Indexed Tree)(也称Fenwick树): 动态维护前缀和
     *
     * 参考: https://www.bilibili.com/video/BV1pE41197Qj/?vd_source=fa6990445a0e68c4b18e85e07647ab23
     *
     * @param nums
     * @return
     */
    private ArrayList<Integer> solution2(ArrayList<Integer> nums){
        int n = nums.size();
        this.result = new int[n];

        // 离散化处理
        discretization(nums);

        // 初始化
        init(n+1);

        int num;
        // 从后往前遍历
        for(int i=n-1; i>=0; i--){
            num = nums.get(i);
            // 树状数组id
            int id = getId(num);
            // 根据id获取前缀和 严格小于(id-1)
            result[i] += query(id-1);
            // 更新前缀和
            update(id);
        }

        ArrayList<Integer> list = new ArrayList<Integer>();
        for(int cnt : result){
            list.add(cnt);
        }

        return list;
    }

    // 树状数组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
     */
    private void update(int pos) {
        while (pos < t.length) {
            // 新增1次
            t[pos] += 1;
            // 当前节点的父节点
            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;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 排序: 归并
     * @param nums
     * @return
     */
    private ArrayList<Integer> solution3(ArrayList<Integer> nums){
        int n = nums.size();

        Integer[] numsArr = new Integer[n];
        nums.toArray(numsArr);

        this.result = new int[n];
        this.index = new int[n];
        for(int i=0; i<n; i++){
            index[i] = i;
        }

        mergeSort(numsArr, 0, n-1);

        ArrayList<Integer> list = new ArrayList<Integer>();
        for(int cnt : result){
            list.add(cnt);
        }

        return list;
    }

    /**
     * 归并排序
     * @param nums
     * @param left
     * @param right
     * @return
     */
    private void mergeSort(Integer[] nums, int left, int right) {
        if(left >= right) {
            return;
        }

        int mid = left+((right-left)>>1);

        mergeSort(nums, left, mid);
        mergeSort(nums, mid+1, right);
        merge(nums, left, mid, right);
    }

    /**
     * 合并
     * @param nums
     * @param left
     * @param mid
     * @param right
     * @return
     */
    private void merge(Integer[] nums, int left, int mid, int right) {
        int len = right-left+1;
        int[] helpIdx = new int[len];
        int[] helpNum = new int[len];
        int i = 0;
        int p = left;
        int q = mid+1;
        while(p<=mid && q<=right) {
            if(nums[p] <= nums[q]){
                result[index[p]] += q-mid-1;
                helpIdx[i] = index[p];
                helpNum[i] = nums[p];
                i++;
                p++;
            }else{
                helpIdx[i] = index[q];
                helpNum[i] = nums[q];
                i++;
                q++;
            }
        }
        while(p <= mid){
            result[index[p]] += q-mid-1;
            helpIdx[i] = index[p];
            helpNum[i] = nums[p];
            i++;
            p++;
        }
        while(q <= right){
            helpIdx[i] = index[q];
            helpNum[i] = nums[q];
            i++;
            q++;
        }
        for(i=0; i<len; i++){
            index[left+i] = helpIdx[i];
            nums[left+i] = helpNum[i];
        }
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 线段树(Segment Tree): 数组实现(堆式存储)
     * @param nums
     * @return
     */
    private ArrayList<Integer> solution4(ArrayList<Integer> nums){
        n = nums.size();
        result = new int[n];
        segmentTree = new int[n*4];

        // 离散化处理
        discrete(nums);

        int num;
        // 从后往前遍历
        for(int i=n-1; i>=0; i--){
            num = nums.get(i);
            // 线段树id
            int id = getIdByNum(num);
            // 根据id获取区间和 严格小于(id-1) range: left<=right
            result[i] += sumRange(0, id-1);
            // 更新前缀和
            update(id, 1);
        }

        ArrayList<Integer> list = new ArrayList<Integer>();
        for(int cnt : result){
            list.add(cnt);
        }

        return list;
    }

    // 线段树
    private int[] segmentTree;
    // 将nums离散化, 去重+排序, 转化为数组a
    private int[] d;
    private int n;

    /**
     * 更新: 根据id进行更新
     * @param id
     * @param val
     */
    public void update(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);
        }
    }

    /**
     * 离散化处理
     *
     * 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;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 线段树(Segment Tree): 树实现
     * @param nums
     * @return
     */
    private ArrayList<Integer> solution5(ArrayList<Integer> nums){
        n = nums.size();
        result = new int[n];
        TreeNode root = new TreeNode(0, n);

        // 离散化处理
        discrete(nums);

        int num;
        // 从后往前遍历
        for(int i=n-1; i>=0; i--){
            num = nums.get(i);
            // 线段树id
            int id = getIdByNum(num);
            // 根据id获取区间和 严格小于(id-1) range: left<=right
            result[i] += sumRange(root, 0, id-1);
            // 更新前缀和
            update(root, id, 1);
        }

        ArrayList<Integer> list = new ArrayList<Integer>();
        for(int cnt : result){
            list.add(cnt);
        }

        return list;
    }

    /**
     * 更新: 根据id进行更新
     * @param root
     * @param id
     * @param val
     */
    public void update(TreeNode root, int id, int val) {
        change(id, val, root);
    }

    /**
     * 区间和
     * @param root
     * @param left
     * @param right
     * @return
     */
    public int sumRange(TreeNode root, int left, int right) {
        return range(left, right, root);
    }

    /**
     * 递归: 更新线段树
     * @param index
     * @param val
     * @param root
     */
    private void change(int index, int val, TreeNode root) {
        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(index, val, root.left);
        }else{
            change(index, val, root.right);
        }

        root.sum = root.left.sum+root.right.sum;
    }
    
    /**
     * 递归: 区间和
     * @param left
     * @param right
     * @param root
     * @return
     */
    private int range(int left, int right, TreeNode root) {
        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(left, right, root.left);
        }else if(left > mid){
            return range(left, right, root.right);
        }else{
            return range(left, mid, root.left)+range(mid+1, right, root.right);
        }
    }

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