/*
QUESTION 6: Walking Through Illuminated Regions
Problem Statement:
You are given N lamps on a 2D plane.
Each lamp lights a circular region.
You are given:
- center of every lamp
- radius of every lamp
- start point
- end point
You can walk only inside lit areas.
You may move from one lamp region to another if the two circles overlap or touch.
Return whether it is possible to travel from start to end without stepping into darkness.
Hard Constraints:
1 <= N <= 1000
Coordinates may be decimal.
-1e6 <= coordinate values <= 1e6
1 <= radius <= 1e6
Example:
lamps:
1. center = (0, 0), radius = 2
2. center = (3, 0), radius = 2
3. center = (7, 0), radius = 1
start = (0, 1)
end = (7, 0)
Expected Output:
false
Explanation:
The first two circles overlap.
The third circle is separated.
So the start and end are not in the same connected lit region.
Brute Force:
Start from all lamps containing the start point.
DFS through overlapping lamps.
If any visited lamp contains the end point, return true.
Time Complexity:
O(N^2)
Optimized Approach:
Treat each lamp as a graph node.
Connect two lamps if:
distance between centers <= r1 + r2
Also connect:
start point to every lamp containing it
end point to every lamp containing it
Use DSU to check if start and end belong to the same component.
Time Complexity:
O(N^2)
Space Complexity:
O(N)
*/
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
struct DSU {
vector<int> parent, size;
DSU(int n) {
parent.resize(n);
size.assign(n, 1);
iota(parent.begin(), parent.end(), 0);
}
int find(int x) {
if (parent[x] == x) return x;
return parent[x] = find(parent[x]);
}
void unite(int a, int b) {
a = find(a);
b = find(b);
if (a == b) return;
if (size[a] < size[b]) swap(a, b);
parent[b] = a;
size[a] += size[b];
}
};
struct Lamp {
long double x, y, r;
};
bool inside(long double px, long double py, const Lamp& c) {
long double dx = px - c.x;
long double dy = py - c.y;
return dx * dx + dy * dy <= c.r * c.r + 1e-12L;
}
bool overlap(const Lamp& a, const Lamp& b) {
long double dx = a.x - b.x;
long double dy = a.y - b.y;
long double sumR = a.r + b.r;
return dx * dx + dy * dy <= sumR * sumR + 1e-12L;
}
bool canWalk(vector<vector<long double>>& data,
long double xs,
long double ys,
long double xt,
long double yt) {
int n = data.size();
vector<Lamp> lamps(n);
for (int i = 0; i < n; i++) {
lamps[i] = {data[i][0], data[i][1], data[i][2]};
}
int START = n;
int END = n + 1;
DSU dsu(n + 2);
for (int i = 0; i < n; i++) {
if (inside(xs, ys, lamps[i])) dsu.unite(START, i);
if (inside(xt, yt, lamps[i])) dsu.unite(END, i);
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (overlap(lamps[i], lamps[j])) {
dsu.unite(i, j);
}
}
}
return dsu.find(START) == dsu.find(END);
}
};
int main() {
Solution sol;
vector<vector<long double>> lamps = {
{0, 0, 2},
{3, 0, 2},
{7, 0, 1}
};
bool ans = sol.canWalk(lamps, 0, 1, 7, 0);
cout << (ans ? "true" : "false") << endl;
return 0;
}
LyoKUVVFU1RJT04gNjogV2Fsa2luZyBUaHJvdWdoIElsbHVtaW5hdGVkIFJlZ2lvbnMKClByb2JsZW0gU3RhdGVtZW50OgpZb3UgYXJlIGdpdmVuIE4gbGFtcHMgb24gYSAyRCBwbGFuZS4KCkVhY2ggbGFtcCBsaWdodHMgYSBjaXJjdWxhciByZWdpb24uCgpZb3UgYXJlIGdpdmVuOgotIGNlbnRlciBvZiBldmVyeSBsYW1wCi0gcmFkaXVzIG9mIGV2ZXJ5IGxhbXAKLSBzdGFydCBwb2ludAotIGVuZCBwb2ludAoKWW91IGNhbiB3YWxrIG9ubHkgaW5zaWRlIGxpdCBhcmVhcy4KWW91IG1heSBtb3ZlIGZyb20gb25lIGxhbXAgcmVnaW9uIHRvIGFub3RoZXIgaWYgdGhlIHR3byBjaXJjbGVzIG92ZXJsYXAgb3IgdG91Y2guCgpSZXR1cm4gd2hldGhlciBpdCBpcyBwb3NzaWJsZSB0byB0cmF2ZWwgZnJvbSBzdGFydCB0byBlbmQgd2l0aG91dCBzdGVwcGluZyBpbnRvIGRhcmtuZXNzLgoKSGFyZCBDb25zdHJhaW50czoKMSA8PSBOIDw9IDEwMDAKQ29vcmRpbmF0ZXMgbWF5IGJlIGRlY2ltYWwuCi0xZTYgPD0gY29vcmRpbmF0ZSB2YWx1ZXMgPD0gMWU2CjEgPD0gcmFkaXVzIDw9IDFlNgoKRXhhbXBsZToKbGFtcHM6CjEuIGNlbnRlciA9ICgwLCAwKSwgcmFkaXVzID0gMgoyLiBjZW50ZXIgPSAoMywgMCksIHJhZGl1cyA9IDIKMy4gY2VudGVyID0gKDcsIDApLCByYWRpdXMgPSAxCgpzdGFydCA9ICgwLCAxKQplbmQgPSAoNywgMCkKCkV4cGVjdGVkIE91dHB1dDoKZmFsc2UKCkV4cGxhbmF0aW9uOgpUaGUgZmlyc3QgdHdvIGNpcmNsZXMgb3ZlcmxhcC4KVGhlIHRoaXJkIGNpcmNsZSBpcyBzZXBhcmF0ZWQuClNvIHRoZSBzdGFydCBhbmQgZW5kIGFyZSBub3QgaW4gdGhlIHNhbWUgY29ubmVjdGVkIGxpdCByZWdpb24uCgpCcnV0ZSBGb3JjZToKU3RhcnQgZnJvbSBhbGwgbGFtcHMgY29udGFpbmluZyB0aGUgc3RhcnQgcG9pbnQuCkRGUyB0aHJvdWdoIG92ZXJsYXBwaW5nIGxhbXBzLgpJZiBhbnkgdmlzaXRlZCBsYW1wIGNvbnRhaW5zIHRoZSBlbmQgcG9pbnQsIHJldHVybiB0cnVlLgoKVGltZSBDb21wbGV4aXR5OgpPKE5eMikKCk9wdGltaXplZCBBcHByb2FjaDoKVHJlYXQgZWFjaCBsYW1wIGFzIGEgZ3JhcGggbm9kZS4KCkNvbm5lY3QgdHdvIGxhbXBzIGlmOgpkaXN0YW5jZSBiZXR3ZWVuIGNlbnRlcnMgPD0gcjEgKyByMgoKQWxzbyBjb25uZWN0OgpzdGFydCBwb2ludCB0byBldmVyeSBsYW1wIGNvbnRhaW5pbmcgaXQKZW5kIHBvaW50IHRvIGV2ZXJ5IGxhbXAgY29udGFpbmluZyBpdAoKVXNlIERTVSB0byBjaGVjayBpZiBzdGFydCBhbmQgZW5kIGJlbG9uZyB0byB0aGUgc2FtZSBjb21wb25lbnQuCgpUaW1lIENvbXBsZXhpdHk6Ck8oTl4yKQoKU3BhY2UgQ29tcGxleGl0eToKTyhOKQoqLwoKI2luY2x1ZGUgPGJpdHMvc3RkYysrLmg+CnVzaW5nIG5hbWVzcGFjZSBzdGQ7CgpjbGFzcyBTb2x1dGlvbiB7CnB1YmxpYzoKICAgIHN0cnVjdCBEU1UgewogICAgICAgIHZlY3RvcjxpbnQ+IHBhcmVudCwgc2l6ZTsKCiAgICAgICAgRFNVKGludCBuKSB7CiAgICAgICAgICAgIHBhcmVudC5yZXNpemUobik7CiAgICAgICAgICAgIHNpemUuYXNzaWduKG4sIDEpOwogICAgICAgICAgICBpb3RhKHBhcmVudC5iZWdpbigpLCBwYXJlbnQuZW5kKCksIDApOwogICAgICAgIH0KCiAgICAgICAgaW50IGZpbmQoaW50IHgpIHsKICAgICAgICAgICAgaWYgKHBhcmVudFt4XSA9PSB4KSByZXR1cm4geDsKICAgICAgICAgICAgcmV0dXJuIHBhcmVudFt4XSA9IGZpbmQocGFyZW50W3hdKTsKICAgICAgICB9CgogICAgICAgIHZvaWQgdW5pdGUoaW50IGEsIGludCBiKSB7CiAgICAgICAgICAgIGEgPSBmaW5kKGEpOwogICAgICAgICAgICBiID0gZmluZChiKTsKCiAgICAgICAgICAgIGlmIChhID09IGIpIHJldHVybjsKCiAgICAgICAgICAgIGlmIChzaXplW2FdIDwgc2l6ZVtiXSkgc3dhcChhLCBiKTsKCiAgICAgICAgICAgIHBhcmVudFtiXSA9IGE7CiAgICAgICAgICAgIHNpemVbYV0gKz0gc2l6ZVtiXTsKICAgICAgICB9CiAgICB9OwoKICAgIHN0cnVjdCBMYW1wIHsKICAgICAgICBsb25nIGRvdWJsZSB4LCB5LCByOwogICAgfTsKCiAgICBib29sIGluc2lkZShsb25nIGRvdWJsZSBweCwgbG9uZyBkb3VibGUgcHksIGNvbnN0IExhbXAmIGMpIHsKICAgICAgICBsb25nIGRvdWJsZSBkeCA9IHB4IC0gYy54OwogICAgICAgIGxvbmcgZG91YmxlIGR5ID0gcHkgLSBjLnk7CgogICAgICAgIHJldHVybiBkeCAqIGR4ICsgZHkgKiBkeSA8PSBjLnIgKiBjLnIgKyAxZS0xMkw7CiAgICB9CgogICAgYm9vbCBvdmVybGFwKGNvbnN0IExhbXAmIGEsIGNvbnN0IExhbXAmIGIpIHsKICAgICAgICBsb25nIGRvdWJsZSBkeCA9IGEueCAtIGIueDsKICAgICAgICBsb25nIGRvdWJsZSBkeSA9IGEueSAtIGIueTsKCiAgICAgICAgbG9uZyBkb3VibGUgc3VtUiA9IGEuciArIGIucjsKCiAgICAgICAgcmV0dXJuIGR4ICogZHggKyBkeSAqIGR5IDw9IHN1bVIgKiBzdW1SICsgMWUtMTJMOwogICAgfQoKICAgIGJvb2wgY2FuV2Fsayh2ZWN0b3I8dmVjdG9yPGxvbmcgZG91YmxlPj4mIGRhdGEsCiAgICAgICAgICAgICAgICAgbG9uZyBkb3VibGUgeHMsCiAgICAgICAgICAgICAgICAgbG9uZyBkb3VibGUgeXMsCiAgICAgICAgICAgICAgICAgbG9uZyBkb3VibGUgeHQsCiAgICAgICAgICAgICAgICAgbG9uZyBkb3VibGUgeXQpIHsKCiAgICAgICAgaW50IG4gPSBkYXRhLnNpemUoKTsKCiAgICAgICAgdmVjdG9yPExhbXA+IGxhbXBzKG4pOwoKICAgICAgICBmb3IgKGludCBpID0gMDsgaSA8IG47IGkrKykgewogICAgICAgICAgICBsYW1wc1tpXSA9IHtkYXRhW2ldWzBdLCBkYXRhW2ldWzFdLCBkYXRhW2ldWzJdfTsKICAgICAgICB9CgogICAgICAgIGludCBTVEFSVCA9IG47CiAgICAgICAgaW50IEVORCA9IG4gKyAxOwoKICAgICAgICBEU1UgZHN1KG4gKyAyKTsKCiAgICAgICAgZm9yIChpbnQgaSA9IDA7IGkgPCBuOyBpKyspIHsKICAgICAgICAgICAgaWYgKGluc2lkZSh4cywgeXMsIGxhbXBzW2ldKSkgZHN1LnVuaXRlKFNUQVJULCBpKTsKICAgICAgICAgICAgaWYgKGluc2lkZSh4dCwgeXQsIGxhbXBzW2ldKSkgZHN1LnVuaXRlKEVORCwgaSk7CiAgICAgICAgfQoKICAgICAgICBmb3IgKGludCBpID0gMDsgaSA8IG47IGkrKykgewogICAgICAgICAgICBmb3IgKGludCBqID0gaSArIDE7IGogPCBuOyBqKyspIHsKICAgICAgICAgICAgICAgIGlmIChvdmVybGFwKGxhbXBzW2ldLCBsYW1wc1tqXSkpIHsKICAgICAgICAgICAgICAgICAgICBkc3UudW5pdGUoaSwgaik7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIHJldHVybiBkc3UuZmluZChTVEFSVCkgPT0gZHN1LmZpbmQoRU5EKTsKICAgIH0KfTsKCmludCBtYWluKCkgewogICAgU29sdXRpb24gc29sOwoKICAgIHZlY3Rvcjx2ZWN0b3I8bG9uZyBkb3VibGU+PiBsYW1wcyA9IHsKICAgICAgICB7MCwgMCwgMn0sCiAgICAgICAgezMsIDAsIDJ9LAogICAgICAgIHs3LCAwLCAxfQogICAgfTsKCiAgICBib29sIGFucyA9IHNvbC5jYW5XYWxrKGxhbXBzLCAwLCAxLCA3LCAwKTsKCiAgICBjb3V0IDw8IChhbnMgPyAidHJ1ZSIgOiAiZmFsc2UiKSA8PCBlbmRsOwoKICAgIHJldHVybiAwOwp9