Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2e16292

Browse files
committed
feat: add solutions to lc problem: No.1201
No.1201.Ugly Number III
1 parent c20050b commit 2e16292

File tree

7 files changed

+355
-200
lines changed

7 files changed

+355
-200
lines changed

‎solution/1200-1299/1201.Ugly Number III/README.md‎

Lines changed: 133 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,21 @@
5656

5757
<!-- 这里可写通用的实现逻辑 -->
5858

59-
**方法一:二分搜索**
59+
**方法一:二分查找 + 容斥原理**
6060

61-
根据题目提示结果在 [1, 2 * 10<sup>9</sup>] 的闭区间上,所以定义二分搜索的左边界 left=1,右边界 right=2e9。此时我们只需要在 [left,right] 的闭区间内找到一个最小的数 num,使其满足 [1,num] 内的丑数总数等于 n,则 num 就是第 n 个丑数。计算在 [1,num] 的范围内丑数的数目,即可以被 a、b 或 c 任意一个数整除的数的总数,其方法如下:
61+
我们可以将题目转换为:找到最小的正整数 $x,ドル使得小于等于 $x$ 的丑数个数恰好为 $n$ 个。
6262

63-
`f(num, a, b, c) = num/a + num/b + num/c - a⋂b - a⋂c - b⋂c + a⋂b⋂c`
63+
对于一个正整数 $x,ドル能被 $a$ 整除的数有 $\left\lfloor \frac{x}{a} \right\rfloor$ 个,能被 $b$ 整除的数有 $\left\lfloor \frac{x}{b} \right\rfloor$ 个,能被 $c$ 整除的数有 $\left\lfloor \frac{x}{c} \right\rfloor$ 个,能被 $a$ 和 $b$ 同时整除的数有 $\left\lfloor \frac{x}{lcm(a, b)} \right\rfloor$ 个,能被 $a$ 和 $c$ 同时整除的数有 $\left\lfloor \frac{x}{lcm(a, c)} \right\rfloor$ 个,能被 $b$ 和 $c$ 同时整除的数有 $\left\lfloor \frac{x}{lcm(b, c)} \right\rfloor$ 个,能被 $a,ドル $b$ 和 $c$ 同时整除的数有 $\left\lfloor \frac{x}{lcm(a, b, c)} \right\rfloor$ 个。根据容斥原理,小于等于 $x$ 的丑数个数为:
6464

65-
- num/a 表示在 [1,num] 内可以整除 a 的数目,num/b 表示在 [1,num] 内可以整除 b 的数目,num/c 表示在 [1,num] 内可以整除 c 的数目。
66-
- a⋂b 表示在 [1,num] 内可以同时整除 a 和 b 的数目,a⋂c 表示在 [1,num] 内可以同时整除 a 和 c 的数,b⋂c 表示在 [1,num] 内可以同时整除 b 和 c 的数。
67-
- a⋂b⋂c 表示在 [1,num] 内可以同时整除 a、b 和 c 的数。
68-
- a⋂b = num/least_common_multiple(a, b),其他情况依次类推。
65+
$$
66+
\left\lfloor \frac{x}{a} \right\rfloor + \left\lfloor \frac{x}{b} \right\rfloor + \left\lfloor \frac{x}{c} \right\rfloor - \left\lfloor \frac{x}{lcm(a, b)} \right\rfloor - \left\lfloor \frac{x}{lcm(a, c)} \right\rfloor - \left\lfloor \frac{x}{lcm(b, c)} \right\rfloor + \left\lfloor \frac{x}{lcm(a, b, c)} \right\rfloor
67+
$$
68+
69+
我们可以使用二分查找的方法找到最小的正整数 $x,ドル使得小于等于 $x$ 的丑数个数恰好为 $n$ 个。
70+
71+
定义二分查找的左边界为 $l=1,ドル右边界为 $r=2 \times 10^9,ドル其中 2ドル \times 10^9$ 是题目给定的最大值。在二分查找的每一步中,我们找出中间数 $mid,ドル如果小于等于 $mid$ 的丑数个数大于等于 $n,ドル那么说明最小的正整数 $x$ 落在 $[l,mid]$ 区间内,否则落在 $[mid+1,r]$ 区间内。在二分查找的过程中,我们需要不断更新小于等于 $mid$ 的丑数个数,直到找到最小的正整数 $x$。
72+
73+
时间复杂度 $O(\log m),ドル其中 $m = 2 \times 10^9$。空间复杂度 $O(1)$。
6974

7075
<!-- tabs:start -->
7176

@@ -75,98 +80,152 @@
7580

7681
```python
7782
class Solution:
78-
def f(self, num: int, a: int, b: int, c: int) -> int:
79-
return num // a + num // b + num // c - num // math.lcm(a, b) - num // math.lcm(a, c) - num // math.lcm(b, c) \
80-
+ num // math.lcm(a, b, c)
81-
8283
def nthUglyNumber(self, n: int, a: int, b: int, c: int) -> int:
83-
left, right = 1, int(2e9)
84-
while left <= right:
85-
mid = left + (right - left) // 2
86-
if self.f(mid, a, b, c) < n:
87-
left = mid + 1
84+
ab = lcm(a, b)
85+
bc = lcm(b, c)
86+
ac = lcm(a, c)
87+
abc = lcm(a, b, c)
88+
l, r = 1, 2 * 10**9
89+
while l < r:
90+
mid = (l + r) >> 1
91+
if mid // a + mid // b + mid // c - mid // ab - mid // bc - mid // ac + mid // abc >= n:
92+
r = mid
8893
else:
89-
right = mid - 1
90-
return left
94+
l = mid + 1
95+
return l
9196
```
9297

93-
### **Go**
98+
### **Java**
9499

95100
<!-- 这里可写当前语言的特殊实现逻辑 -->
96101

97-
```go
98-
func nthUglyNumber(n int, a int, b int, c int) int {
99-
left, right := 1, int(2e9)
100-
for left <= right {
101-
mid := left + (right-left)/2
102-
if f(mid, a, b, c) < n {
103-
left = mid + 1
104-
} else {
105-
right = mid - 1
106-
}
107-
}
108-
return left
109-
}
102+
```java
103+
class Solution {
104+
public int nthUglyNumber(int n, int a, int b, int c) {
105+
long ab = lcm(a, b);
106+
long bc = lcm(b, c);
107+
long ac = lcm(a, c);
108+
long abc = lcm(ab, c);
109+
long l = 1, r = 2000000000;
110+
while (l < r) {
111+
long mid = (l + r) >> 1;
112+
if (mid / a + mid / b + mid / c - mid / ab - mid / bc - mid / ac + mid / abc >= n) {
113+
r = mid;
114+
} else {
115+
l = mid + 1;
116+
}
117+
}
118+
return (int) l;
119+
}
110120

111-
funcf(numint, aint, bint, cint) int {
112-
return num/a + num/b + num/c - num/lcm(a, b) - num/lcm(a, c) - num/lcm(b, c) + num/lcm(lcm(a, b), c)
113-
}
121+
privatelonggcd(longa, longb) {
122+
return b ==0? a : gcd(b, a % b);
123+
}
114124

115-
// Least common multiple
116-
func lcm(a, b int) int {
117-
// Greatest common divisor
118-
gcd := func(x, y int) int {
119-
for y != 0 {
120-
if x < y {
121-
x, y = y, x
122-
}
123-
x, y = y, x%y
124-
}
125-
return x
126-
}
127-
return a * b / gcd(a, b)
125+
private long lcm(long a, long b) {
126+
return a * b / gcd(a, b);
127+
}
128128
}
129129
```
130130

131131
### **C++**
132132

133-
<!-- 这里可写当前语言的特殊实现逻辑 -->
134-
135133
```cpp
136134
class Solution {
137135
public:
138-
long gcd(long x, long y) {
139-
while (y != 0) {
140-
if (x < y)
141-
swap(x, y);
142-
long tmp = x % y;
143-
x = y;
144-
y = tmp;
136+
int nthUglyNumber(int n, int a, int b, int c) {
137+
long long ab = lcm(a, b);
138+
long long bc = lcm(b, c);
139+
long long ac = lcm(a, c);
140+
long long abc = lcm(ab, c);
141+
long long l = 1, r = 2000000000;
142+
while (l < r) {
143+
long long mid = (l + r) >> 1;
144+
if (mid / a + mid / b + mid / c - mid / ab - mid / bc - mid / ac + mid / abc >= n) {
145+
r = mid;
146+
} else {
147+
l = mid + 1;
148+
}
145149
}
146-
return x;
150+
return l;
147151
}
148152

149-
long lcm(long x, long y) { return x * y / gcd(x, y); }
153+
long long lcm(long long a, long long b) {
154+
return a * b / gcd(a, b);
155+
}
150156

151-
long f(int num, int a, int b, int c) {
152-
long sumabc = long(num / a) + num / b + num / c;
153-
long intersections = long(num / lcm(a, b)) + num / lcm(a, c) + num / lcm(b, c) - num / lcm(lcm(a, b), c);
154-
return sumabc - intersections;
157+
long long gcd(long long a, long long b) {
158+
return b == 0 ? a : gcd(b, a % b);
155159
}
160+
};
161+
```
156162
157-
int nthUglyNumber(int n, int a, int b, int c) {
158-
int left = 1, right = int(2e9);
159-
while (left <= right) {
160-
int mid = left + (right - left) / 2;
161-
if (f(mid, a, b, c) < n) {
162-
left = mid + 1;
163-
} else {
164-
right = mid - 1;
165-
}
163+
### **Go**
164+
165+
```go
166+
func nthUglyNumber(n int, a int, b int, c int) int {
167+
ab, bc, ac := lcm(a, b), lcm(b, c), lcm(a, c)
168+
abc := lcm(ab, c)
169+
var l, r int = 1, 2e9
170+
for l < r {
171+
mid := (l + r) >> 1
172+
if mid/a+mid/b+mid/c-mid/ab-mid/bc-mid/ac+mid/abc >= n {
173+
r = mid
174+
} else {
175+
l = mid + 1
176+
}
177+
}
178+
return l
179+
}
180+
181+
func gcd(a, b int) int {
182+
if b == 0 {
183+
return a
184+
}
185+
return gcd(b, a%b)
186+
}
187+
188+
func lcm(a, b int) int {
189+
return a * b / gcd(a, b)
190+
}
191+
```
192+
193+
### **TypeScript**
194+
195+
```ts
196+
function nthUglyNumber(n: number, a: number, b: number, c: number): number {
197+
const ab = lcm(BigInt(a), BigInt(b));
198+
const bc = lcm(BigInt(b), BigInt(c));
199+
const ac = lcm(BigInt(a), BigInt(c));
200+
const abc = lcm(BigInt(a), bc);
201+
let l = 1n;
202+
let r = BigInt(2e9);
203+
while (l < r) {
204+
const mid = (l + r) >> 1n;
205+
const count =
206+
mid / BigInt(a) +
207+
mid / BigInt(b) +
208+
mid / BigInt(c) -
209+
mid / ab -
210+
mid / bc -
211+
mid / ac +
212+
mid / abc;
213+
if (count >= BigInt(n)) {
214+
r = mid;
215+
} else {
216+
l = mid + 1n;
166217
}
167-
return left;
168218
}
169-
};
219+
return Number(l);
220+
}
221+
222+
function gcd(a: bigint, b: bigint): bigint {
223+
return b === 0n ? a : gcd(b, a % b);
224+
}
225+
226+
function lcm(a: bigint, b: bigint): bigint {
227+
return (a * b) / gcd(a, b);
228+
}
170229
```
171230

172231
### **...**

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /