|
80 | 80 |
|
81 | 81 | ## 解法
|
82 | 82 |
|
83 |
| -### 方法一 |
| 83 | +### 方法一:二分查找 + 容斥原理 |
| 84 | + |
| 85 | +我们可以将题目转化为:找到最小的正整数 $x$,使得小于等于 $x$ 的且满足条件的数的个数恰好为 $k$ 个。如果 $x$ 满足条件,那么对任意 $x' > x$ 的 $x'$ 也满足条件,这存在单调性,因此我们可以使用二分查找,找到最小的满足条件的 $x$。 |
| 86 | + |
| 87 | +我们定义一个函数 `check(x)`,用来判断小于等于 $x$ 的且满足条件的数的个数是否大于等于 $k$。我们需要计算有多少个数可以由 $coins$ 中的数组合得到。 |
| 88 | + |
| 89 | +假设 $coins$ 为 $[a, b]$,根据容斥原理,小于等于 $x$ 的满足条件的数的个数为: |
| 90 | + |
| 91 | +$$ |
| 92 | +\left\lfloor \frac{x}{a} \right\rfloor + \left\lfloor \frac{x}{b} \right\rfloor - \left\lfloor \frac{x}{lcm(a, b)} \right\rfloor |
| 93 | +$$ |
| 94 | + |
| 95 | +如果 $coins$ 为 $[a, b, c]$,小于等于 $x$ 的满足条件的数的个数为: |
| 96 | + |
| 97 | +$$ |
| 98 | +\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 |
| 99 | +$$ |
| 100 | + |
| 101 | +可以看到,我们需要累加所有任意奇数个数的情况,减去所有任意偶数个数的情况。 |
| 102 | + |
| 103 | +由于 $n \leq 15$,我们可以使用二进制枚举的方式,枚举所有的子集,计算满足条件的数的个数,我们记为 $cnt$。如果 $cnt \geq k$,那么我们需要找到最小的 $x$,使得 $check(x)$ 为真。 |
| 104 | + |
| 105 | +在二分查找开始时,我们定义二分查找的左边界 $l=1$,右边界 $r={10}^{11}$,然后我们不断地将中间值 $mid$ 代入 `check` 函数中,如果 `check(mid)` 为真,那么我们将右边界 $r$ 更新为 $mid$,否则我们将左边界 $l$ 更新为 $mid+1$。最终返回 $l$。 |
| 106 | + |
| 107 | +时间复杂度 $O(n \times 2^n \times \log (k \times M))$,其中 $n$ 是数组 $coins$ 的长度,而 $M$ 是数组 $coins$ 中的最大值。 |
84 | 108 |
|
85 | 109 | <!-- tabs:start -->
|
86 | 110 |
|
87 | 111 | ```python
|
88 |
| - |
| 112 | +class Solution: |
| 113 | + def findKthSmallest(self, coins: List[int], k: int) -> int: |
| 114 | + def check(mx: int) -> bool: |
| 115 | + cnt = 0 |
| 116 | + for i in range(1, 1 << len(coins)): |
| 117 | + v = 1 |
| 118 | + for j, x in enumerate(coins): |
| 119 | + if i >> j & 1: |
| 120 | + v = lcm(v, x) |
| 121 | + if v > mx: |
| 122 | + break |
| 123 | + m = i.bit_count() |
| 124 | + if m & 1: |
| 125 | + cnt += mx // v |
| 126 | + else: |
| 127 | + cnt -= mx // v |
| 128 | + return cnt >= k |
| 129 | + |
| 130 | + return bisect_left(range(10**11), True, key=check) |
89 | 131 | ```
|
90 | 132 |
|
91 | 133 | ```java
|
92 |
| - |
| 134 | +class Solution { |
| 135 | + private int[] coins; |
| 136 | + private int k; |
| 137 | + |
| 138 | + public long findKthSmallest(int[] coins, int k) { |
| 139 | + this.coins = coins; |
| 140 | + this.k = k; |
| 141 | + long l = 1, r = (long) 1e11; |
| 142 | + while (l < r) { |
| 143 | + long mid = (l + r) >> 1; |
| 144 | + if (check(mid)) { |
| 145 | + r = mid; |
| 146 | + } else { |
| 147 | + l = mid + 1; |
| 148 | + } |
| 149 | + } |
| 150 | + return l; |
| 151 | + } |
| 152 | + |
| 153 | + private boolean check(long mx) { |
| 154 | + long cnt = 0; |
| 155 | + int n = coins.length; |
| 156 | + for (int i = 1; i < 1 << n; ++i) { |
| 157 | + long v = 1; |
| 158 | + for (int j = 0; j < n; ++j) { |
| 159 | + if ((i >> j & 1) == 1) { |
| 160 | + v = lcm(v, coins[j]); |
| 161 | + if (v > mx) { |
| 162 | + break; |
| 163 | + } |
| 164 | + } |
| 165 | + } |
| 166 | + int m = Integer.bitCount(i); |
| 167 | + if (m % 2 == 1) { |
| 168 | + cnt += mx / v; |
| 169 | + } else { |
| 170 | + cnt -= mx / v; |
| 171 | + } |
| 172 | + } |
| 173 | + return cnt >= k; |
| 174 | + } |
| 175 | + |
| 176 | + private long lcm(long a, long b) { |
| 177 | + return a * b / gcd(a, b); |
| 178 | + } |
| 179 | + |
| 180 | + private long gcd(long a, long b) { |
| 181 | + return b == 0 ? a : gcd(b, a % b); |
| 182 | + } |
| 183 | +} |
93 | 184 | ```
|
94 | 185 |
|
95 | 186 | ```cpp
|
96 |
| - |
| 187 | +class Solution { |
| 188 | +public: |
| 189 | + long long findKthSmallest(vector<int>& coins, int k) { |
| 190 | + using ll = long long; |
| 191 | + ll l = 1, r = 1e11; |
| 192 | + int n = coins.size(); |
| 193 | + |
| 194 | + auto check = [&](ll mx) { |
| 195 | + ll cnt = 0; |
| 196 | + for (int i = 1; i < 1 << n; ++i) { |
| 197 | + ll v = 1; |
| 198 | + for (int j = 0; j < n; ++j) { |
| 199 | + if (i >> j & 1) { |
| 200 | + v = lcm(v, coins[j]); |
| 201 | + if (v > mx) { |
| 202 | + break; |
| 203 | + } |
| 204 | + } |
| 205 | + } |
| 206 | + int m = __builtin_popcount(i); |
| 207 | + if (m & 1) { |
| 208 | + cnt += mx / v; |
| 209 | + } else { |
| 210 | + cnt -= mx / v; |
| 211 | + } |
| 212 | + } |
| 213 | + return cnt >= k; |
| 214 | + }; |
| 215 | + |
| 216 | + while (l < r) { |
| 217 | + ll mid = (l + r) >> 1; |
| 218 | + if (check(mid)) { |
| 219 | + r = mid; |
| 220 | + } else { |
| 221 | + l = mid + 1; |
| 222 | + } |
| 223 | + } |
| 224 | + return l; |
| 225 | + } |
| 226 | +}; |
97 | 227 | ```
|
98 | 228 |
|
99 | 229 | ```go
|
| 230 | +func findKthSmallest(coins []int, k int) int64 { |
| 231 | + var r int = 1e11 |
| 232 | + n := len(coins) |
| 233 | + ans := sort.Search(r, func(mx int) bool { |
| 234 | + cnt := 0 |
| 235 | + for i := 1; i < 1<<n; i++ { |
| 236 | + v := 1 |
| 237 | + for j, x := range coins { |
| 238 | + if i>>j&1 == 1 { |
| 239 | + v = lcm(v, x) |
| 240 | + if v > mx { |
| 241 | + break |
| 242 | + } |
| 243 | + } |
| 244 | + } |
| 245 | + m := bits.OnesCount(uint(i)) |
| 246 | + if m%2 == 1 { |
| 247 | + cnt += mx / v |
| 248 | + } else { |
| 249 | + cnt -= mx / v |
| 250 | + } |
| 251 | + } |
| 252 | + return cnt >= k |
| 253 | + }) |
| 254 | + return int64(ans) |
| 255 | +} |
| 256 | + |
| 257 | +func gcd(a, b int) int { |
| 258 | + if b == 0 { |
| 259 | + return a |
| 260 | + } |
| 261 | + return gcd(b, a%b) |
| 262 | +} |
| 263 | + |
| 264 | +func lcm(a, b int) int { |
| 265 | + return a * b / gcd(a, b) |
| 266 | +} |
| 267 | +``` |
100 | 268 |
|
| 269 | +```ts |
| 270 | +function findKthSmallest(coins: number[], k: number): number { |
| 271 | + let [l, r] = [1n, BigInt(1e11)]; |
| 272 | + const n = coins.length; |
| 273 | + const check = (mx: bigint): boolean => { |
| 274 | + let cnt = 0n; |
| 275 | + for (let i = 1; i < 1 << n; ++i) { |
| 276 | + let v = 1n; |
| 277 | + for (let j = 0; j < n; ++j) { |
| 278 | + if ((i >> j) & 1) { |
| 279 | + v = lcm(v, BigInt(coins[j])); |
| 280 | + if (v > mx) { |
| 281 | + break; |
| 282 | + } |
| 283 | + } |
| 284 | + } |
| 285 | + const m = bitCount(i); |
| 286 | + if (m & 1) { |
| 287 | + cnt += mx / v; |
| 288 | + } else { |
| 289 | + cnt -= mx / v; |
| 290 | + } |
| 291 | + } |
| 292 | + return cnt >= BigInt(k); |
| 293 | + }; |
| 294 | + while (l < r) { |
| 295 | + const mid = (l + r) >> 1n; |
| 296 | + if (check(mid)) { |
| 297 | + r = mid; |
| 298 | + } else { |
| 299 | + l = mid + 1n; |
| 300 | + } |
| 301 | + } |
| 302 | + return Number(l); |
| 303 | +} |
| 304 | + |
| 305 | +function gcd(a: bigint, b: bigint): bigint { |
| 306 | + return b === 0n ? a : gcd(b, a % b); |
| 307 | +} |
| 308 | + |
| 309 | +function lcm(a: bigint, b: bigint): bigint { |
| 310 | + return (a * b) / gcd(a, b); |
| 311 | +} |
| 312 | + |
| 313 | +function bitCount(i: number): number { |
| 314 | + i = i - ((i >>> 1) & 0x55555555); |
| 315 | + i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); |
| 316 | + i = (i + (i >>> 4)) & 0x0f0f0f0f; |
| 317 | + i = i + (i >>> 8); |
| 318 | + i = i + (i >>> 16); |
| 319 | + return i & 0x3f; |
| 320 | +} |
101 | 321 | ```
|
102 | 322 |
|
103 | 323 | <!-- tabs:end -->
|
|
0 commit comments