|
| 1 | +const std = @import("std"); |
| 2 | + |
| 3 | +const sqrt = std.math.sqrt; |
| 4 | +const abs = std.math.absInt; |
| 5 | +const max = std.math.max; |
| 6 | +const min = std.math.min; |
| 7 | + |
| 8 | +fn floor(x: f64) i32 { |
| 9 | + return @floatToInt(i32, @floor(x)); |
| 10 | +} |
| 11 | + |
| 12 | +fn ceil(x: f64) i32 { |
| 13 | + return @floatToInt(i32, @ceil(x)); |
| 14 | +} |
| 15 | + |
| 16 | +fn float(x: i32) f64 { |
| 17 | + return @intToFloat(f64, x); |
| 18 | +} |
| 19 | + |
| 20 | +fn divCeil(x: i32, y: i32) i32 { |
| 21 | + const res = @divFloor(x, y); |
| 22 | + return if (res * y == x) res else res + 1; |
| 23 | +} |
| 24 | + |
| 25 | +pub fn main() !void { |
| 26 | + var file = try std.fs.cwd().openFile("input", .{}); |
| 27 | + defer file.close(); |
| 28 | + |
| 29 | + var buf_reader = std.io.bufferedReader(file.reader()); |
| 30 | + var in_stream = buf_reader.reader(); |
| 31 | + |
| 32 | + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 33 | + defer arena.deinit(); |
| 34 | + |
| 35 | + const allocator = &arena.allocator; |
| 36 | + var read_buf: [1024]u8 = undefined; |
| 37 | + |
| 38 | + const line = (try in_stream.readUntilDelimiterOrEof(&read_buf, '\n')).?; |
| 39 | + // e.g. "target area: x=20..30, y=-10..-5" |
| 40 | + |
| 41 | + var split = std.mem.split(line["target area: ".len..], ", "); |
| 42 | + var x_str = std.mem.split(split.next().?["x=".len..], ".."); |
| 43 | + const x1 = try std.fmt.parseInt(i32, x_str.next().?, 10); |
| 44 | + const x2 = try std.fmt.parseInt(i32, x_str.next().?, 10); |
| 45 | + |
| 46 | + var y_str = std.mem.split(split.next().?["y=".len..], ".."); |
| 47 | + const y1 = try std.fmt.parseInt(i32, y_str.next().?, 10); |
| 48 | + const y2 = try std.fmt.parseInt(i32, y_str.next().?, 10); |
| 49 | + |
| 50 | + // s: step |
| 51 | + // x: initial x velocity |
| 52 | + // y: initial y velocity |
| 53 | + |
| 54 | + // f(s,x) = |
| 55 | + // if (s < x) -(s-x)(s-x-1)/2 + x(x+1)/2 |
| 56 | + // if (s >= x) x(x+1)/2 |
| 57 | + |
| 58 | + // find max y such that there exists an s, there exists an x |
| 59 | + // y1 <= (-s^2 + 2sy + s)/2 <= y2 |
| 60 | + // x1 <= f(s,x) <= x2 |
| 61 | + |
| 62 | + // We hit target with y iff there exist a step s: |
| 63 | + // y1 <= -(s-y)(s-y-1)/2 + y*(y+1)/2 <= y2 <==> |
| 64 | + // y1 <= (-s*s + s*y + s + y*s)/2 <= y2 <==> |
| 65 | + // y1 <= (-s^2 + s*(2*y + 1))/2 <= y2 <==> |
| 66 | + // 0 <= -s^2 + s*(2*y + 1) - 2*y1 |
| 67 | + // and 0 >= -s^2 + s*(2*y + 1) - 2*y2 |
| 68 | + |
| 69 | + // solve for s: |
| 70 | + // y1s1 = (2*y + 1 - sqrt(4*y^2 + 4*y + 1 - 8*y1)) / 2 |
| 71 | + // y1s2 = (2*y + 1 + sqrt(4*y^2 + 4*y + 1 - 8*y1)) / 2 |
| 72 | + |
| 73 | + // y2s1 = (2*y + 1 - sqrt(4*y^2 + 4*y + 1 - 8*y2)) / 2 |
| 74 | + // y2s1 = (2*y + 1 + sqrt(4*y^2 + 4*y + 1 - 8*y2)) / 2 |
| 75 | + |
| 76 | + // y1s1 <= s <= y1s2 and (y2s2 <= s or s <= y2s1) <==> |
| 77 | + // max(y2s2,y1s1) <= s <= y1s2 or y1s1 <= s <= min(y1s2,y2s1) |
| 78 | + |
| 79 | + // Otherwise we cannot hit target with y: |
| 80 | + const highest_y: i32 = max(try abs(y1), try abs(y2)); |
| 81 | + const lowest_y: i32 = -highest_y; |
| 82 | + |
| 83 | + var y: i32 = highest_y; |
| 84 | + var s: i32 = undefined; |
| 85 | + var num: u32 = 0; |
| 86 | + while (y >= lowest_y) : (y -= 1) { |
| 87 | + const y1s1 = (float(2 * y + 1) - sqrt(float(4 * y * y + 4 * y + 1 - 8 * y1))) / 2; |
| 88 | + const y1s2 = (float(2 * y + 1) + sqrt(float(4 * y * y + 4 * y + 1 - 8 * y1))) / 2; |
| 89 | + |
| 90 | + const y2s1 = (float(2 * y + 1) - sqrt(float(4 * y * y + 4 * y + 1 - 8 * y2))) / 2; |
| 91 | + const y2s2 = (float(2 * y + 1) + sqrt(float(4 * y * y + 4 * y + 1 - 8 * y2))) / 2; |
| 92 | + |
| 93 | + var xs = std.AutoHashMap(i32, void).init(allocator); |
| 94 | + defer xs.deinit(); |
| 95 | + |
| 96 | + // iterate s |
| 97 | + s = max(max(ceil(y2s2), ceil(y1s1)), 0); |
| 98 | + while (s <= floor(y1s2)) : (s += 1) { |
| 99 | + const intervals = intervalX(y, s, x1, x2); |
| 100 | + for (intervals) |interval| { |
| 101 | + var x: i32 = interval.from; |
| 102 | + while (x <= interval.to) : (x += 1) { |
| 103 | + try xs.put(x, {}); |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | + s = max(ceil(y1s1), 0); |
| 108 | + while (s <= min(floor(y1s2), floor(y2s1))) : (s += 1) { |
| 109 | + const intervals = intervalX(y, s, x1, x2); |
| 110 | + for (intervals) |interval| { |
| 111 | + var x: i32 = interval.from; |
| 112 | + while (x <= interval.to) : (x += 1) { |
| 113 | + try xs.put(x, {}); |
| 114 | + } |
| 115 | + } |
| 116 | + } |
| 117 | + num += xs.count(); |
| 118 | + } |
| 119 | + |
| 120 | + const stdout = std.io.getStdOut().writer(); |
| 121 | + try stdout.print("{d}\n", .{num}); |
| 122 | +} |
| 123 | + |
| 124 | +const Interval = struct { |
| 125 | + from: i32, |
| 126 | + to: i32, |
| 127 | +}; |
| 128 | + |
| 129 | +fn intervalX(y: i32, s: i32, x1: i32, x2: i32) [3]Interval { |
| 130 | + var intervals: [3]Interval = undefined; |
| 131 | + // Solve for x: |
| 132 | + // if s < x: |
| 133 | + // x1 <= sum x-s+1 .. x <= x2 <==> |
| 134 | + // x1 <= s*x - (s-1)*s/2 <= x2 <==> |
| 135 | + // x1/s + (s - 1)/2 <= x <= x2/s + (s - 1)/2 |
| 136 | + |
| 137 | + var num: i32 = 0; |
| 138 | + { |
| 139 | + const min_x = max(divCeil(x1 + @divExact((s - 1) * s, 2), s), s + 1); |
| 140 | + const max_x = @divFloor(x2 + @divExact((s - 1) * s, 2), s); |
| 141 | + intervals[0] = Interval{ .from = min_x, .to = max_x }; |
| 142 | + } |
| 143 | + |
| 144 | + // if s <= x: |
| 145 | + // x1 <= (x*x + x) / 2 <= x2 |
| 146 | + // Solve: |
| 147 | + // sx1 = (-1 - sqrt(1 + 8*x1)) / 2 |
| 148 | + // sx2 = (-1 + sqrt(1 + 8*x1)) / 2 |
| 149 | + // |
| 150 | + // sx3 = (-1 - sqrt(1 + 8*x2)) / 2 |
| 151 | + // sx4 = (-1 + sqrt(1 + 8*x2)) / 2 |
| 152 | + // |
| 153 | + // so: |
| 154 | + // (x <= sx1 or sx2 <= x) and |
| 155 | + // (sx3 <= x <= sx4) <==> |
| 156 | + // (max(sx3, s) <= x <= min(sx1, sx4)) or |
| 157 | + // (max(sx2, sx3, s) <= x <= sx4) |
| 158 | + { |
| 159 | + const sx1 = floor((-1 - sqrt(float(1 + 8 * x1))) / 2); |
| 160 | + const sx2 = ceil((-1 + sqrt(float(1 + 8 * x1))) / 2); |
| 161 | + const sx3 = ceil((-1 - sqrt(float(1 + 8 * x2))) / 2); |
| 162 | + const sx4 = floor((-1 + sqrt(float(1 + 8 * x2))) / 2); |
| 163 | + { |
| 164 | + const min_x = max(sx3, 0); |
| 165 | + const max_x = min(min(sx1, sx4), s); |
| 166 | + intervals[1] = Interval{ .from = min_x, .to = max_x }; |
| 167 | + } |
| 168 | + { |
| 169 | + const min_x = max(max(sx2, sx3), 0); |
| 170 | + const max_x = min(sx4, s); |
| 171 | + intervals[2] = Interval{ .from = min_x, .to = max_x }; |
| 172 | + } |
| 173 | + } |
| 174 | + return intervals; |
| 175 | +} |
0 commit comments