[CodeForces] Moving Points

Problem Statement Link

 

Clarification:

The problem states that for each pair of points, we consider the minimum possible distance over any possible moments of time, including non-integer time interval. This means each different pair is independent, and we are only adding up each different pairs' possible minimum distance. This is different with taking a snap shot of a certain time, then add all pair distances at that time. 

 

Analysis: 

For a pair (Xj, Xi) such that Xj < Xi, if Vj <= Vi, D(Xj, Xi) is minimal at time 0, the distance will only increase over time; If Vj > Vi, then D(Xj, Xi) = 0, because Xj will eventually catch up with Xi. So we for each point Xi, its contribution as the bigger point initially toward the result distance sum only involves points Xj to its left with Vj <= Vi. 

 

The naive approach takes O(N^2) time. Call the contribution of Xi as the bigger point S, S = (Xi - X1) + (Xi - X2) + ...... + (Xi - Xj), assuming X1 to Xj are all the points to Xi's left with a no greater speed. Denote Cnt as the number of such left points, S = Xi * Cnt - (X1 + X2 + ... + Xj) = Xi * Cnt - Sum(X1....Xj). The problem now is to get Cnt and Sum(X1, Xj). 

 

To do this, we can use an dynamic data structure such as Binary Indexed Tree. The problem here is that both X and V can get as big as 10^8. A naive BIT implementation will consume too much memory. To avoid this, we apply a technique called coordinate compression on Speed V. This basically ranks all speed, since we only have up to O(10^5) different speeds, we are good with memory consumption. 

 

Algorithm:

1.  Compress all speeds to a relative ranking in range [1, N].

2. Combine points and their relative speed and sort on point position in ascending order. 

3.  Create two BIT cntBit and sumBit. cntBit is used to get the count of points to a given point's left, with a no greater speed. sumBit is used to get the sum of point positions to a given points' left, with a no greater speed. 

4. Iterate all points from left to right, get cnt from cntBit and sum from sumBit, compute the current total sum, then update both BIT.  

 

Because we process points in ascending order, it is guranteed that for each given point, we only consider points to its left. The coordinate compression ensures both BIT use relative speed ranking as their key. So a range sum query on cntBit using the given point P's ranking speed is equivalent to ask for the count of points that are located to the left of P with speed in [1, P's relative speed]. 

A range sum query on sumBit using P's ranking speed is to ask for the prefix sum of all such left points. 

 

The running time of such algorithm is O(N*logN) with O(N) space complexity.

    private static void solve(int q, FastScanner in, PrintWriter out) {
        for (int qq = 0; qq < q; qq++) {
            int n = in.nextInt();
            int[] x = new int[n];
            for(int i = 0; i < n; i++) {
                x[i] = in.nextInt();
            }
            Integer[] v = new Integer[n];
            for(int i = 0;i < n; i++) {
                v[i] = in.nextInt();
            }
            v = ArrayUtils.compress(v);

            BinaryIndexedTree cntBit = new BinaryIndexedTree(n);
            BinaryIndexedTree sumBit = new BinaryIndexedTree(n);
            int[][] p = new int[n][2];
            for(int i = 0; i < n; i++) {
                p[i][0] = x[i];
                p[i][1] = v[i];
            }
            Arrays.sort(p, Comparator.comparingInt(a -> a[0]));
            long sum = 0;
            for(int i = 0; i < n; i++) {
                long prevCnt = cntBit.rangeSum(p[i][1]);
                long prevSum = sumBit.rangeSum(p[i][1]);
                sum += (prevCnt * p[i][0] - prevSum);
                cntBit.adjust(p[i][1], 1);
                sumBit.adjust(p[i][1], p[i][0]);
            }
            out.println(sum);
        }
        out.close();
    }

    private static class ArrayUtils {
        /*
            Compress all values of a to its rank in [1, a.length]
         */
        private static Integer[] compress(Integer[] a) {
            int n = a.length;
            Integer[] ret = new Integer[n];
            Integer[] copy = Arrays.copyOf(a, n);
            Arrays.sort(copy);
            int rank = 1;
            Map<Integer, Integer> map = new HashMap<>();
            for(int v : copy) {
                if(!map.containsKey(v)) {
                    map.put(v, rank);
                    rank++;
                }
            }
            for(int i = 0; i < n; i++) {
                ret[i] = map.get(a[i]);
            }
            return ret;
        }
    }

    private static class BinaryIndexedTree {
        private long[] ft;

        private BinaryIndexedTree(int n) {
            ft = new long[n + 1];
        }

        private long rangeSum(int l, int r) {
            return rangeSum(r) - (l == 1 ? 0 : rangeSum(l - 1));
        }

        private long rangeSum(int r) {
            long sum = 0;
            for(; r > 0; r -= leastSignificantOne(r)) {
                sum += ft[r];
            }
            return sum;
        }

        private void adjust(int k, int diff) {
            for(; k < ft.length; k += leastSignificantOne(k)) {
                ft[k] += diff;
            }
        }
        private int leastSignificantOne(int i) {
            return i & (-i);
        }
    }

 

 

Related Problems

[LeetCode] 1395. Count Number of Teams

posted @ 2020-02-28 23:07  Review->Improve  阅读(329)  评论(0编辑  收藏  举报