Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Stop thinking of an operation as placing an arbitrary point. Think of it as: from one existing edge, you get two new existing edges, so you can keep walking along the newest edge.
The basic gadget is two triangles forming a parallelogram cell. If edge exists and a vector is safe, add , then . Now the translated edge exists too.
For a long segment of length , split it into equal vectors. Each step has length in for the useful cases; the only remaining headache is the cross edge of each little parallelogram.
That cross edge is automatically legal when the travel direction makes an angle between and with the current base edge. Too shallow or too steep is not fatal; first create a fixed point at angle or .
Do not overfit a giant angle-case if-tree. Build the five canonical strips: middle from , middle from , shallow kickoff, and two steep kickoffs. Simulate them and print the first valid one. The case proof says one survives; the validator catches the annoying ceiling crap.
Let , , and . The limit is operations.
The reusable gadget Suppose an edge already exists and we want to translate it by a vector . Do two operations:
Now edge exists, so the original edge has been shifted by . These two triangles form one parallelogram cell.
Every side length used in this cell is one of three values: , , and . The first one is already legal because is an existing edge. So the whole game is choosing so that and the cross edge is also in .
For any long segment of length , taking and gives . In all accepted strip constructions, , so also .
When a straight strip works Start with the base edge , whose length is exactly . If makes angle with the positive -axis, the cross edge from to has length
If , this length is always between and . Geometrically: below it cannot get longer than , and above the distance from to the travel ray is at least .
So if the ray has a middle angle, we split into pieces and build a strip whose left rail ends at . This uses operations.
Similarly, if the ray has a middle angle, we build the same strip in direction , and the right rail ends at .
What if the target is too shallow? If both relevant rays are below , a direct strip hugs the -axis too tightly, so the cross edge becomes shorter than . We first create
which is a legal point from edge : and .
Then choose a point on the ray/line from toward such that when possible; if that intersection would put farther than one unit from , use the first point on at distance from . The triangle is legal. From edge , build a parallelogram strip parallel to until the lower rail reaches .
Why does the budget survive the two setup moves? In this shallow region, is noticeably closer to than is: informally, moving the start one unit right saves more than half a unit of remaining distance. That slack pays for the kickoff. The small fractional-length cases are exactly why the implementation validates the candidate instead of trusting vibes. Vibes are how you get WA on a 3500.
What if the target is too steep? Now direct motion from is above . We first create the other equilateral-ish point
Then we try two strips from edge :
One of these handles the ceiling issue. If has a friendly fractional part, the first strip fits. If it is in the annoying range where that construction loses one operation, the second strip shortens the remaining rail by essentially one unit and fits instead.
Implementation strategy The cleanest code does not manually classify angles. It builds five candidate constructions:
After each candidate, simulate it: check that the chosen edge already exists, both new side lengths are in , the target point was created, and the number of operations is at most . The proof above guarantees at least one candidate passes; the validator makes the code much less fragile around floating-point and ceiling boundaries.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
using ld = long double;
struct Pt {
ld x, y;
};
struct Op {
int u, v, w;
};
Pt operator + (Pt a, Pt b) { return {a.x + b.x, a.y + b.y}; }
Pt operator - (Pt a, Pt b) { return {a.x - b.x, a.y - b.y}; }
Pt operator * (Pt a, ld k) { return {a.x * k, a.y * k}; }
ld dot(Pt a, Pt b) { return a.x * b.x + a.y * b.y; }
ld cross(Pt a, Pt b) { return a.x * b.y - a.y * b.x; }
ld norm(Pt a) { return sqrtl(dot(a, a)); }
Pt target;
int limitOps;
vector<Pt> pts;
vector<Op> ops;
pair<int,int> edgeKey(int a, int b) {
if (a > b) swap(a, b);
return {a, b};
}
void resetBuild() {
pts.assign(3, {0, 0});
pts[1] = {0, 0};
pts[2] = {1, 0};
ops.clear();
}
int addPoint(int u, int v, Pt z) {
int id = (int)pts.size();
pts.push_back(z);
ops.push_back({u, v, id});
return id;
}
int pieces(Pt v) {
return max(0, (int)ceill(norm(v) - 1e-9L));
}
Pt projectPoint(Pt x, Pt a, Pt b) {
Pt v = b - a;
return a + v * (dot(v, x - a) / dot(v, v));
}
ld distLine(Pt x, Pt a, Pt b) {
return fabsl(cross(a - x, b - x)) / norm(b - a);
}
Pt lineCircleForward(Pt o, Pt a, Pt b) {
Pt v = b - a;
Pt pj = projectPoint(o, a, b);
ld d = distLine(o, a, b);
ld h2 = 1.0L - d * d;
if (h2 < 0) h2 = 0;
return pj + v * (sqrtl(h2) / norm(v));
}
bool validBuild() {
if ((int)ops.size() > limitOps) return false;
set<pair<int,int>> edges;
edges.insert({1, 2});
bool hit = false;
for (int i = 0; i < (int)ops.size(); i++) {
auto [u, v, w] = ops[i];
if (w != i + 3) return false;
if (u < 1 || v < 1 || u > i + 2 || v > i + 2 || u == v) return false;
if (!edges.count(edgeKey(u, v))) return false;
ld a = norm(pts[u] - pts[w]);
ld b = norm(pts[v] - pts[w]);
if (a < 0.5L - 1e-8L || a > 1.0L + 1e-8L) return false;
if (b < 0.5L - 1e-8L || b > 1.0L + 1e-8L) return false;
edges.insert(edgeKey(u, w));
edges.insert(edgeKey(v, w));
if (norm(pts[w] - target) <= 1e-4L) hit = true;
}
return hit;
}
bool build1a() {
resetBuild();
Pt dir = target - pts[1];
int s = pieces(dir);
for (int i = 1; i < s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[1] + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[2] + dir * t);
}
addPoint((int)pts.size() - 2, (int)pts.size() - 1, target);
return validBuild();
}
bool build1b() {
resetBuild();
Pt dir = target - pts[2];
int s = pieces(dir);
for (int i = 1; i <= s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[1] + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[2] + dir * t);
}
return validBuild();
}
bool build2() {
resetBuild();
Pt d = {sqrtl(3.0L) / 2.0L, 0.5L};
addPoint(1, 2, d);
Pt e = lineCircleForward(d, pts[2], target);
if (norm(e - pts[2]) > 1.0L) {
Pt dir0 = target - pts[2];
e = pts[2] + dir0 * (1.0L / norm(dir0));
}
addPoint(2, 3, e);
Pt dir = target - e;
int s = pieces(dir);
for (int i = 1; i <= s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, d + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, e + dir * t);
}
return validBuild();
}
bool build3a() {
resetBuild();
Pt d = {0.5L, sqrtl(3.0L) / 2.0L};
addPoint(1, 2, d);
Pt dir = target - pts[2];
int s = pieces(dir);
for (int i = 1; i < s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[2] + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, d + dir * t);
}
addPoint((int)pts.size() - 2, (int)pts.size() - 1, target);
return validBuild();
}
bool build3b() {
resetBuild();
Pt d = {0.5L, sqrtl(3.0L) / 2.0L};
addPoint(1, 2, d);
Pt dir = target - d;
int s = pieces(dir);
for (int i = 1; i <= s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[2] + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, d + dir * t);
}
return validBuild();
}
int main() {
setIO();
int T;
cin >> T;
cout << fixed << setprecision(12);
while (T--) {
int p, q, m;
cin >> p >> q >> m;
target = {(ld)p, (ld)q};
limitOps = m;
bool ok = build1a();
if (!ok) ok = build1b();
if (!ok) ok = build2();
if (!ok) ok = build3a();
if (!ok) ok = build3b();
assert(ok);
cout << ops.size() << '\n';
for (auto [u, v, w] : ops) {
cout << u << ' ' << v << ' ' << (double)pts[w].x << ' ' << (double)pts[w].y << '\n';
}
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
using ld = long double;
struct Pt {
ld x, y;
};
struct Op {
int u, v, w;
};
Pt operator + (Pt a, Pt b) { return {a.x + b.x, a.y + b.y}; }
Pt operator - (Pt a, Pt b) { return {a.x - b.x, a.y - b.y}; }
Pt operator * (Pt a, ld k) { return {a.x * k, a.y * k}; }
ld dot(Pt a, Pt b) { return a.x * b.x + a.y * b.y; }
ld cross(Pt a, Pt b) { return a.x * b.y - a.y * b.x; }
ld norm(Pt a) { return sqrtl(dot(a, a)); }
Pt target;
int limitOps;
vector<Pt> pts;
vector<Op> ops;
pair<int,int> edgeKey(int a, int b) {
if (a > b) swap(a, b);
return {a, b};
}
void resetBuild() {
pts.assign(3, {0, 0});
pts[1] = {0, 0};
pts[2] = {1, 0};
ops.clear();
}
int addPoint(int u, int v, Pt z) {
int id = (int)pts.size();
pts.push_back(z);
ops.push_back({u, v, id});
return id;
}
int pieces(Pt v) {
return max(0, (int)ceill(norm(v) - 1e-9L));
}
Pt projectPoint(Pt x, Pt a, Pt b) {
Pt v = b - a;
return a + v * (dot(v, x - a) / dot(v, v));
}
ld distLine(Pt x, Pt a, Pt b) {
return fabsl(cross(a - x, b - x)) / norm(b - a);
}
Pt lineCircleForward(Pt o, Pt a, Pt b) {
Pt v = b - a;
Pt pj = projectPoint(o, a, b);
ld d = distLine(o, a, b);
ld h2 = 1.0L - d * d;
if (h2 < 0) h2 = 0;
return pj + v * (sqrtl(h2) / norm(v));
}
bool validBuild() {
if ((int)ops.size() > limitOps) return false;
set<pair<int,int>> edges;
edges.insert({1, 2});
bool hit = false;
for (int i = 0; i < (int)ops.size(); i++) {
auto [u, v, w] = ops[i];
if (w != i + 3) return false;
if (u < 1 || v < 1 || u > i + 2 || v > i + 2 || u == v) return false;
if (!edges.count(edgeKey(u, v))) return false;
ld a = norm(pts[u] - pts[w]);
ld b = norm(pts[v] - pts[w]);
if (a < 0.5L - 1e-8L || a > 1.0L + 1e-8L) return false;
if (b < 0.5L - 1e-8L || b > 1.0L + 1e-8L) return false;
edges.insert(edgeKey(u, w));
edges.insert(edgeKey(v, w));
if (norm(pts[w] - target) <= 1e-4L) hit = true;
}
return hit;
}
bool build1a() {
resetBuild();
Pt dir = target - pts[1];
int s = pieces(dir);
for (int i = 1; i < s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[1] + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[2] + dir * t);
}
addPoint((int)pts.size() - 2, (int)pts.size() - 1, target);
return validBuild();
}
bool build1b() {
resetBuild();
Pt dir = target - pts[2];
int s = pieces(dir);
for (int i = 1; i <= s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[1] + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[2] + dir * t);
}
return validBuild();
}
bool build2() {
resetBuild();
Pt d = {sqrtl(3.0L) / 2.0L, 0.5L};
addPoint(1, 2, d);
Pt e = lineCircleForward(d, pts[2], target);
if (norm(e - pts[2]) > 1.0L) {
Pt dir0 = target - pts[2];
e = pts[2] + dir0 * (1.0L / norm(dir0));
}
addPoint(2, 3, e);
Pt dir = target - e;
int s = pieces(dir);
for (int i = 1; i <= s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, d + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, e + dir * t);
}
return validBuild();
}
bool build3a() {
resetBuild();
Pt d = {0.5L, sqrtl(3.0L) / 2.0L};
addPoint(1, 2, d);
Pt dir = target - pts[2];
int s = pieces(dir);
for (int i = 1; i < s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[2] + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, d + dir * t);
}
addPoint((int)pts.size() - 2, (int)pts.size() - 1, target);
return validBuild();
}
bool build3b() {
resetBuild();
Pt d = {0.5L, sqrtl(3.0L) / 2.0L};
addPoint(1, 2, d);
Pt dir = target - d;
int s = pieces(dir);
for (int i = 1; i <= s; i++) {
ld t = (ld)i / s;
addPoint((int)pts.size() - 2, (int)pts.size() - 1, pts[2] + dir * t);
addPoint((int)pts.size() - 2, (int)pts.size() - 1, d + dir * t);
}
return validBuild();
}
int main() {
setIO();
int T;
cin >> T;
cout << fixed << setprecision(12);
while (T--) {
int p, q, m;
cin >> p >> q >> m;
target = {(ld)p, (ld)q};
limitOps = m;
bool ok = build1a();
if (!ok) ok = build1b();
if (!ok) ok = build2();
if (!ok) ok = build3a();
if (!ok) ok = build3b();
assert(ok);
cout << ops.size() << '\n';
for (auto [u, v, w] : ops) {
cout << u << ' ' << v << ' ' << (double)pts[w].x << ' ' << (double)pts[w].y << '\n';
}
}
}