Skip to content

3015. Count the Number of Houses at a Certain Distance I 👍

  • Time: $O(n)$
  • Space: $O(n)$
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
class Solution {
 public:
  vector<int> countOfPairs(int n, int x, int y) {
    if (x > y)
      swap(x, y);

    const int ringLen = y - x + 1;
    const int leftLineLen = x - 1;
    const int rightLineLen = n - y;

    vector<int> ans(n);
    ans = addVectors(ans, bothInRing(n, ringLen));
    ans = addVectors(ans, bothInTheSameLine(n, leftLineLen));
    ans = addVectors(ans, bothInTheSameLine(n, rightLineLen));
    ans = addVectors(ans, lineToRing(n, leftLineLen, ringLen));
    ans = addVectors(ans, lineToRing(n, rightLineLen, ringLen));
    ans = addVectors(ans, lineToLine(n, x, y, leftLineLen, rightLineLen));
    for (int& freq : ans)
      freq *= 2;
    return ans;
  }

 private:
  // Returns the contribution from the scenario where two houses are located in
  // the ring.
  vector<int> bothInRing(int n, int ringLen) {
    vector<int> res(n);
    for (int k = 1; k <= (ringLen - 1) / 2; ++k)
      res[k - 1] += ringLen;
    if (ringLen % 2 == 0)
      res[ringLen / 2 - 1] += ringLen / 2;
    return res;
  }

  // Returns the contribution from the scenario where two houses are either
  // located in the left line [1, x) or the right line (y, n].
  vector<int> bothInTheSameLine(int n, int lineLen) {
    vector<int> res(n);
    for (int k = 1; k <= lineLen; ++k)
      res[k - 1] += lineLen - k;
    return res;
  }

  // Returns the contribution from the scenario where one house is either
  // located in the left line [1, x) or the right line (y, n] and the other
  // house is located in the cycle.
  vector<int> lineToRing(int n, int lineLen, int ringLen) {
    vector<int> res(n);
    for (int k = 1; k <= lineLen + ringLen; ++k) {
      // min(
      //   at most k - 1 since we need to give 1 to the line,
      //   at most ringLen / 2 since for length > ringLen / 2, it can always be
      //     calculated as ringLen - ringLen / 2
      // )
      const int maxInRingLen = min(k - 1, ringLen / 2);
      // max(at least 0, at lest k - lineLen)
      const int minInRingLen = max(0, k - lineLen);
      if (minInRingLen <= maxInRingLen) {
        // Each ring length contributes 2 to the count due to the split of
        // paths when entering the ring: One path traverses the upper half of
        // the ring, and the other traverses the lower half.
        // This is illustrated as follows:
        //   Path 1: ... -- x -- (upper half of the ring)
        //   Path 2: ... -- x -- (lower half of the ring)
        res[k - 1] += (maxInRingLen - minInRingLen + 1) * 2;
        if (minInRingLen == 0)
          // Subtract 1 since there's no split.
          res[k - 1] -= 1;
        if (maxInRingLen * 2 == ringLen)
          // Subtract 1 since the following case only contribute one:
          //   ... -- x -- (upper half of the ring) -- middle point
          //   ... -- x -- (upper half of the ring) -- middle point
          res[k - 1] -= 1;
      }
    }
    return res;
  }

  // Returns the contribution from the scenario where one house is in the left
  // line [1, x) and the other house is in the right line (y, n].
  vector<int> lineToLine(int n, int x, int y, int leftLineLen,
                         int rightLineLen) {
    vector<int> res(n);
    for (int k = 1; k <= leftLineLen + rightLineLen + 2; ++k) {
      // min(
      //   at most leftLineLen,
      //   at most k - 1 - (x < y) since we need to give 1 to the right line
      //     and if x < y we need to give another 1 to "x - y".
      // )
      const int maxInLeft = min(leftLineLen, k - 1 - (x < y));
      // max(at least 1, at least k - rightLineLen - (x < y))
      const int minInLeft = max(1, k - rightLineLen - (x < y));
      if (minInLeft <= maxInLeft)
        res[k - 1] += maxInLeft - minInLeft + 1;
    }
    return res;
  }

  vector<int> addVectors(const vector<int>& a, const vector<int>& b) {
    vector<int> res(a.size());
    transform(a.begin(), a.end(), b.begin(), res.begin(), plus<int>());
    return res;
  };
};
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
class Solution {
  public int[] countOfPairs(int n, int x, int y) {
    if (x > y) {
      final int temp = x;
      x = y;
      y = temp;
    }

    final int ringLen = y - x + 1;
    final int leftLineLen = x - 1;
    final int rightLineLen = n - y;

    int[] ans = new int[n];
    ans = addVectors(ans, bothInRing(n, ringLen));
    ans = addVectors(ans, bothInTheSameLine(n, leftLineLen));
    ans = addVectors(ans, bothInTheSameLine(n, rightLineLen));
    ans = addVectors(ans, lineToRing(n, leftLineLen, ringLen));
    ans = addVectors(ans, lineToRing(n, rightLineLen, ringLen));
    ans = addVectors(ans, lineToLine(n, x, y, leftLineLen, rightLineLen));
    for (int i = 0; i < ans.length; ++i)
      ans[i] *= 2;
    return ans;
  }

  // Returns the contribution from the scenario where two houses are located in
  // the ring.
  private int[] bothInRing(int n, int ringLen) {
    int[] res = new int[n];
    for (int k = 1; k <= (ringLen - 1) / 2; ++k)
      res[k - 1] += ringLen;
    if (ringLen % 2 == 0)
      res[ringLen / 2 - 1] += ringLen / 2;
    return res;
  }

  // Returns the contribution from the scenario where two houses are either
  // located in the left line [1, x) or the right line (y, n].
  private int[] bothInTheSameLine(int n, int lineLen) {
    int[] res = new int[n];
    for (int k = 1; k <= lineLen; ++k)
      res[k - 1] += lineLen - k;
    return res;
  }

  // Returns the contribution from the scenario where one house is either
  // located in the left line [1, x) or the right line (y, n] and the other
  // house is located in the cycle.
  private int[] lineToRing(int n, int lineLen, int ringLen) {
    int[] res = new int[n];
    for (int k = 1; k <= lineLen + ringLen; ++k) {
      // min(
      //   at most k - 1 since we need to give 1 to the line,
      //   at most ringLen / 2 since for length > ringLen / 2, it can always be
      //     calculated as ringLen - ringLen / 2
      // )
      final int maxInRingLen = Math.min(k - 1, ringLen / 2);
      // max(at least 0, at lest k - lineLen)
      final int minInRingLen = Math.max(0, k - lineLen);
      if (minInRingLen <= maxInRingLen) {
        // Each ring length contributes 2 to the count due to the split of
        // paths when entering the ring: One path traverses the upper half of
        // the ring, and the other traverses the lower half.
        // This is illustrated as follows:
        //   Path 1: ... -- x -- (upper half of the ring)
        //   Path 2: ... -- x -- (lower half of the ring)
        res[k - 1] += (maxInRingLen - minInRingLen + 1) * 2;
        if (minInRingLen == 0)
          // Subtract 1 since there's no split.
          res[k - 1] -= 1;
        if (maxInRingLen * 2 == ringLen)
          // Subtract 1 since the following case only contribute one:
          //   ... -- x -- (upper half of the ring) -- middle point
          //   ... -- x -- (upper half of the ring) -- middle point
          res[k - 1] -= 1;
      }
    }
    return res;
  }

  // Returns the contribution from the scenario where one house is in the left
  // line [1, x) and the other house is in the right line (y, n].
  private int[] lineToLine(int n, int x, int y, int leftLineLen, int rightLineLen) {
    int[] res = new int[n];
    for (int k = 1; k <= leftLineLen + rightLineLen + 2; ++k) {
      // min(
      //   at most leftLineLen,
      //   at most k - 1 - (x < y) since we need to give 1 to the right line
      //     and if x < y we need to give another 1 to "x - y".
      // )
      final int maxInLeft = Math.min(leftLineLen, k - 1 - (x < y ? 1 : 0));
      // max(at least 1, at least k - rightLineLen - (x < y))
      final int minInLeft = Math.max(1, k - rightLineLen - (x < y ? 1 : 0));
      if (minInLeft <= maxInLeft)
        res[k - 1] += maxInLeft - minInLeft + 1;
    }
    return res;
  }

  private int[] addVectors(int[] a, int[] b) {
    for (int i = 0; i < a.length; ++i)
      a[i] += b[i];
    return a;
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class Solution:
  def countOfPairs(self, n: int, x: int, y: int) -> list[int]:
    if x > y:
      x, y = y, x

    def bothInRing(ringLen: int) -> list[int]:
      """
      Returns the contribution from the scenario where two houses are located
      in the ring.
      """
      res = [0] * n
      for k in range(1, (ringLen - 1) // 2 + 1):
        res[k - 1] += ringLen
      if ringLen % 2 == 0:
        res[ringLen // 2 - 1] += ringLen // 2
      return res

    def bothInTheSameLine(lineLen: int) -> list[int]:
      """
      Returns the contribution from the scenario where two houses are either
      located in the left line [1, x) or the right line (y, n].
      """
      res = [0] * n
      for k in range(1, lineLen + 1):
        res[k - 1] += lineLen - k
      return res

    def lineToRing(lineLen: int, ringLen: int) -> list[int]:
      """
      Returns the contribution from the scenario where one house is either
      located in the left line [1, x) or the right line (y, n] and the
      other house is located in the cycle.
      """
      res = [0] * n
      for k in range(1, lineLen + ringLen):
        # min(
        #   at most k - 1 since we need to give 1 to the line,
        #   at most ringLen / 2 since for length > ringLen / 2, it can always be
        #     calculated as ringLen - ringLen / 2
        # )
        maxInRingLen = min(k - 1, ringLen // 2)
        # max(at least 0, at lest k - lineLen)
        minInRingLen = max(0, k - lineLen)
        if minInRingLen <= maxInRingLen:
          # Each ring length contributes 2 to the count due to the split of
          # paths when entering the ring: One path traverses the upper half of
          # the ring, and the other traverses the lower half.
          # This is illustrated as follows:
          #   Path 1: ... -- x -- (upper half of the ring)
          #   Path 2: ... -- x -- (lower half of the ring)
          res[k - 1] += (maxInRingLen - minInRingLen + 1) * 2
          if minInRingLen == 0:
            # Subtract 1 since there's no split.
            res[k - 1] -= 1
          if maxInRingLen * 2 == ringLen:
            # Subtract 1 since the following case only contribute one:
            #   ... -- x -- (upper half of the ring) -- middle point
            #   ... -- x -- (upper half of the ring) -- middle point
            res[k - 1] -= 1
      return res

    def lineToLine(leftLineLen: int, rightLineLen: int) -> list[int]:
      """
      Returns the contribution from the scenario where one house is in the left
      line [1, x) and the other house is in the right line (y, n].
      """
      res = [0] * n
      for k in range(leftLineLen + rightLineLen + 2):
        # min(
        #   at most leftLineLen,
        #   at most k - 1 - (x < y) since we need to give 1 to the right line
        #     and if x < y we need to give another 1 to "x - y".
        # )
        maxInLeft = min(leftLineLen, k - 1 - (x < y))
        # max(at least 1, at least k - rightLineLen - (x < y))
        minInLeft = max(1, k - rightLineLen - (x < y))
        if minInLeft <= maxInLeft:
          res[k - 1] += maxInLeft - minInLeft + 1
      return res

    ringLen = y - x + 1
    leftLineLen = x - 1
    rightLineLen = (n - y)

    ans = [0] * n
    ans = list(map(operator.add, ans, bothInRing(ringLen)))
    ans = list(map(operator.add, ans, bothInTheSameLine(leftLineLen)))
    ans = list(map(operator.add, ans, bothInTheSameLine(rightLineLen)))
    ans = list(map(operator.add, ans, lineToRing(leftLineLen, ringLen)))
    ans = list(map(operator.add, ans, lineToRing(rightLineLen, ringLen)))
    ans = list(map(operator.add, ans, lineToLine(leftLineLen, rightLineLen)))
    return [freq * 2 for freq in ans]