-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path100-arranged-probability.js
More file actions
275 lines (223 loc) · 7.63 KB
/
100-arranged-probability.js
File metadata and controls
275 lines (223 loc) · 7.63 KB
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
process.stdin.resume();
process.stdin.setEncoding("ascii");
let _input = "";
process.stdin.on("data", function (input) {
_input += input;
});
process.stdin.on("end", function () {
processData(_input);
});
function processData(input) {
const lines = input.trim().split(/\s+/); // Split by any whitespace
let lineIdx = 0;
const T = parseInt(lines[lineIdx++]);
for (let t = 0; t < T; t++) {
const P = BigInt(lines[lineIdx++]);
const Q = BigInt(lines[lineIdx++]);
const D = BigInt(lines[lineIdx++]);
solve(P, Q, D);
}
}
function solve(P, Q, D) {
// 1. Reduce Fraction P/Q
const common = gcd(P, Q);
P = P / common;
Q = Q / common;
// 2. Setup Diophantine: P*X^2 - Q*Y^2 = P - Q
// Let determinant Det = P*Q
const Det = P * Q;
const K = P - Q; // The RHS constant derived from P(2n-1)^2 - Q(2b-1)^2
// Check if Det is a perfect square
const sqrtDet = sqrtBigInt(Det);
if (sqrtDet * sqrtDet === Det) {
solveSquareCase(P, Q, D, sqrtDet);
} else {
solvePellCase(P, Q, D, Det, K);
}
}
// Case where P*Q is a perfect square (Difference of Squares)
function solveSquareCase(P, Q, D, S) {
// Equation: (PX - SY)(PX + SY) = P(P - Q) = Target
// Since P < Q, Target is negative.
// Let's rewrite: (SY - PX)(SY + PX) = P(Q - P) = Target (Positive now)
const Target = P * (Q - P);
// We need to find factors u * v = Target such that u <= v
// Then SY - PX = u, SY + PX = v
// 2*SY = u + v => Y = (u+v)/2S
// 2*PX = v - u => X = (v-u)/2P
// Constraints: (u+v) % 2S == 0, (v-u) % 2P == 0, Y odd, X odd.
// Also n = (X+1)/2 > D.
// Optimization:
// Max n occurs when u is minimized (u=1).
// Approx max 2PX approx v approx Target.
// Max X approx Target/2P = (Q-P)/2.
// Max n approx (Q-P)/4.
// Since Q <= 10^7, max n is around 2.5*10^6.
// If D > 2.5*10^6, it's likely "No solution".
// We iterate u from 1 up to sqrt(Target).
let bestN = null;
let bestB = null;
// Iterate factors
// Since Target can be up to ~10^14, we iterate u up to 10^7. Feasible.
const limit = sqrtBigInt(Target);
for (let u = 1n; u <= limit; u++) {
if (Target % u === 0n) {
const v = Target / u;
// Check if solution exists for pair (u, v)
checkSquareSol(u, v, P, S, D);
}
}
function checkSquareSol(u, v, P, S, D) {
// 2*PX = v - u
const v_minus_u = v - u;
const twoP = 2n * P;
if (v_minus_u % twoP !== 0n) return;
const X = v_minus_u / twoP;
// 2*SY = u + v
const v_plus_u = v + u;
const twoS = 2n * S;
if (v_plus_u % twoS !== 0n) return;
const Y = v_plus_u / twoS;
// Check odd parity (variable substitution requirement)
if (X % 2n === 0n || Y % 2n === 0n) return;
const n = (X + 1n) / 2n;
const b = (Y + 1n) / 2n;
if (n > D) {
if (bestN === null || n < bestN) {
bestN = n;
bestB = b;
}
}
}
if (bestN !== null) {
console.log(bestB.toString() + " " + bestN.toString());
} else {
console.log("No solution");
}
}
// Case where P*Q is not a square (Pell Equation)
function solvePellCase(P, Q, D, Det, K) {
// 1. Find fundamental solution (r, s) to r^2 - Det*s^2 = 1
const fund = getFundamentalPell(Det);
if (!fund) {
// Should not happen for non-square Det
console.log("No solution");
return;
}
const r = fund.x;
const s = fund.y;
// 2. Generate solutions from two seeds:
// Seed 1: (1, 1) -> Represents trivial solution n=1, b=1
// Seed 2: (1, -1) -> Represents conjugate class
let sol1 = findFirstValid(1n, 1n, r, s, P, Q, D);
let sol2 = findFirstValid(1n, -1n, r, s, P, Q, D);
// Select the best valid solution
let finalN = null;
let finalB = null;
if (sol1 && sol2) {
if (sol1.n < sol2.n) {
finalN = sol1.n; finalB = sol1.b;
} else {
finalN = sol2.n; finalB = sol2.b;
}
} else if (sol1) {
finalN = sol1.n; finalB = sol1.b;
} else if (sol2) {
finalN = sol2.n; finalB = sol2.b;
}
if (finalN !== null) {
console.log(finalB.toString() + " " + finalN.toString());
} else {
console.log("No solution");
}
}
// Finds the first solution in a recurrence chain where n > D
function findFirstValid(x0, y0, r, s, P, Q, limitD) {
let x = x0;
let y = y0;
// Recurrence:
// x_new = x*r + y*s*Q
// y_new = y*r + x*s*P
// We iterate until we find a solution > D.
// If numbers get absurdly large without match, we break (though math guarantees eventual growth).
while (true) {
// Calculate next terms
let x_next = x * r + y * s * Q;
let y_next = y * r + x * s * P;
x = x_next;
y = y_next;
// Check validity
// X must be odd (2n-1) and Y must be odd (2b-1)
// Also X must be positive for valid n > 0.
// In the (1, -1) branch, X can be negative initially. We take abs(X) because (-X)^2 = X^2.
let absX = x < 0n ? -x : x;
let absY = y < 0n ? -y : y;
if (absX % 2n !== 0n && absY % 2n !== 0n) {
let n = (absX + 1n) / 2n;
let b = (absY + 1n) / 2n;
if (n > limitD) {
return { n: n, b: b };
}
}
// Safety break for extremely large numbers (solving for limitD ~ 10^15 usually takes few steps)
// If n exceeds limitD significantly, we might have skipped?
// No, we check every step. If we just found one > limitD, we return it immediately.
// Just need to ensure we don't overflow memory or loop forever if logic is wrong.
if (absX > limitD * 100n && absX > 10n**20n) {
// If we are way past D and haven't returned, something is odd,
// but usually the first valid one > D is caught.
// For safety, if we found nothing valid and are super huge, stop.
return null;
}
}
}
// Continued Fraction algorithm to find fundamental solution to x^2 - Dy^2 = 1
function getFundamentalPell(D) {
let m = 0n;
let d = 1n;
let a = sqrtBigInt(D);
let a0 = a;
if (a * a === D) return null; // Should be handled by square case check
let h1 = 1n, h2 = 0n;
let k1 = 0n, k2 = 1n;
// Standard convergent recurrence
while (true) {
// Update convergents
// h_next = a * h1 + h2
// k_next = a * k1 + k2
let h = a * h1 + h2;
let k = a * k1 + k2;
// Check Pell equation: h^2 - D*k^2 = 1
if (h * h - D * k * k === 1n) {
return { x: h, y: k };
}
// Shift for next iteration
h2 = h1; h1 = h;
k2 = k1; k1 = k;
// Update continued fraction terms
m = d * a - m;
d = (D - m * m) / d;
a = (a0 + m) / d;
}
}
// Helper: Integer Square Root for BigInt
function sqrtBigInt(value) {
if (value < 0n) return 0n; // Error handling
if (value < 2n) return value;
let x0 = value;
let x1 = (x0 + value / x0) / 2n;
while (x1 < x0) {
x0 = x1;
x1 = (x0 + value / x0) / 2n;
}
return x0;
}
// Helper: GCD for BigInt
function gcd(a, b) {
while (b > 0n) {
let temp = b;
b = a % b;
a = temp;
}
return a;
}