[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 will 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);
        }
    }

原文地址:https://www.cnblogs.com/lz87/p/12375067.html

时间: 2024-11-09 13:01:26

[CodeForces] Moving Points的相关文章

hdu 4717 The Moving Points(三分)

题目链接:hdu 4717 The Moving Points 题意: 在二维平面上有n个点,每个点给出移动的方向和速度. 问在某个时刻,这些点中最大距离最小是多少,输出时刻和距离. 题解: 我们可以知道,每个点对的距离要么是单调递增,要么是有一个峰的函数. 举例画一下可知道合成的这个函数最多只有一个峰,所以可以用三分求解. 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespa

Codeforces 19D Points 线段树+set

题目链接:点击打开链接 线段树维护y值大于val的最小x值 #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <iostream> #include <map> #include <set> #include <math.h> using namespace std; #define inf

HDU 4717 The Moving Points (三分法)

题意:给n个点的坐标的移动方向及速度,问在之后的时间的所有点的最大距离的最小值是多少. 思路:三分.两点距离是下凹函数,它们的max也是下凹函数.可以三分. #include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<cstdlib> #include<string> #include<cmath> #include&

Codeforces 19D Points(树状数组)

题目链接:Codeforces 19D Points 题目大意:N中操作,每次添加一个点,或者删除一个点,以及找到给定x,y坐标最近的一个坐标,并且保证xi,yi在x,y的右上角. 解题思路:这题的解法还是很机智的. y坐标离散化,然后树状数组的每个单位用一个set代替,set记录的是点集. 剩下的操作就像树状数组一样,每次添加就等于是+w的操作,移除就等于是-w,只是w是一个点,那么find操作就等于是在sum操作生成的点集中二分查找. #include <cstdio> #include

hdu 4717 The Moving Points(三分)

http://acm.hdu.edu.cn/showproblem.php?pid=4717 大致题意:给出每个点的坐标以及每个点移动的速度和方向.问在那一时刻点集中最远的距离在所有时刻的最远距离中最小. 比赛时一直以为是计算几何,和线段相交什么的有关.赛后队友说这是道三分,仔细想了想确实是三分,试着画画图发现它是一个凸性函数,存在一个最短距离.然后三分时间就可以了. #include <stdio.h> #include <iostream> #include <map&g

HDU-4717 The Moving Points(凸函数求极值)

The Moving Points Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2122    Accepted Submission(s): 884 Problem Description There are N points in total. Every point moves in certain direction and

hdu4717The Moving Points(三分)

链接 需要特判一下n=1的时候 精度调太低会超时 1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 1

Codeforces 430A Points and Segments (easy)

题意:让你染色点,要求在给出的区间内|红色个数-蓝色个数|<=1 思路:排序后依次交替染色就能达到效果 #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int MAXN = 110; int arr[MAXN]; int n,m,x,y; int

CodeForces 251A. Points on Line(数学 lower_bound )

题目链接:http://codeforces.com/problemset/problem/251/A Little Petya likes points a lot. Recently his mom has presented him n points lying on the line OX. Now Petya is wondering in how many ways he can choose three distinct points so that the distance be