From 2ef1c273bc0832dac2375e3ddf5ae42dbef6117e Mon Sep 17 00:00:00 2001 From: freakin23 Date: Mon, 16 Dec 2024 19:33:55 +0530 Subject: [PATCH 1/7] py sol for cyclic array --- solutions/platinum/cses-1191.mdx | 87 ++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/solutions/platinum/cses-1191.mdx b/solutions/platinum/cses-1191.mdx index 2945f85f45..541736eadc 100644 --- a/solutions/platinum/cses-1191.mdx +++ b/solutions/platinum/cses-1191.mdx @@ -124,5 +124,92 @@ int main() { printf("%d\n", r); } ``` + + + +```py +MAXN = 200000 +MAXL = 20 # approx maximum log base 2 of n + +# next[i][j] stores the start point of the next +# 2^j'th subarray if we choose this subarray starting at i +nxt = [[0] * MAXL for _ in range(MAXN)] + +# len[i][j] stores the total amount of elements between +# subarray starting at i and the next 2^j'th subarray +length = [[0] * MAXL for _ in range(MAXN)] + + +def len_between(x: int, y: int) -> int: + """ + :returns the number of elements between the subarray + starting at x and the next y subarrays + """ + total = 0 + for i in range(MAXL): + if y & (1 << i): + total += length[x][i] + x = nxt[x][i] + return total + + +def check(x: int) -> bool: + """ + :return true if there is a series of x subarrays + that uses the whole array + """ + for i in range(n): + if len_between(i, x) >= n: + return True + return False + + + +n, k = map(int, input().split()) +a = list(map(int, input().split())) + +total_sum = sum(a) + +# edge case: every element can go into one subarray +if k >= total_sum: + print(1) + exit(0) + +# two pointers to find each subarray range [l, r) +current_sum = 0 +r = 0 + +for l in range(n): + while current_sum + a[r % n] <= k: + current_sum += a[r % n] + r += 1 + + # initialize binary lifting arrays + nxt[l][0] = r % n + length[l][0] = r - l + + current_sum -= a[l] + +for j in range(1, MAXL): + for i in range(n): + nxt[i][j] = nxt[nxt[i][j - 1]][j - 1] + # add lengths from the left and the right + length[i][j] = length[i][j - 1] + length[nxt[i][j - 1]][j - 1] + + +# binary search over the least amount of subarrays +# needed to cover the whole array +lo, hi = 0, n +while lo < hi - 1: + mid = (lo + hi) // 2 + if check(mid): + hi = mid + else: + lo = mid + +print(hi) +``` + + From e40a1afe9291d163c226e5c34b6aa1f92f53f63f Mon Sep 17 00:00:00 2001 From: freakin23 Date: Mon, 16 Dec 2024 19:37:30 +0530 Subject: [PATCH 2/7] add warning for py sol --- solutions/platinum/cses-1191.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/solutions/platinum/cses-1191.mdx b/solutions/platinum/cses-1191.mdx index 541736eadc..af2186109b 100644 --- a/solutions/platinum/cses-1191.mdx +++ b/solutions/platinum/cses-1191.mdx @@ -128,6 +128,12 @@ int main() { + + +Due to Python's constant factor, the below solution TLEs on a couple test cases. + + + ```py MAXN = 200000 MAXL = 20 # approx maximum log base 2 of n From 6964d40ffb09b44d36539da03942196616682210 Mon Sep 17 00:00:00 2001 From: freakin23 Date: Mon, 16 Dec 2024 19:43:52 +0530 Subject: [PATCH 3/7] add new edge case --- solutions/platinum/cses-1191.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/solutions/platinum/cses-1191.mdx b/solutions/platinum/cses-1191.mdx index af2186109b..b17891a2e6 100644 --- a/solutions/platinum/cses-1191.mdx +++ b/solutions/platinum/cses-1191.mdx @@ -177,6 +177,11 @@ a = list(map(int, input().split())) total_sum = sum(a) +# edge case: each element create their own subarray +if k == 1: + print(n) + exit(0) + # edge case: every element can go into one subarray if k >= total_sum: print(1) From f7041a10d48df09184dd01a4815c70c52f769760 Mon Sep 17 00:00:00 2001 From: freakin23 Date: Mon, 16 Dec 2024 19:45:55 +0530 Subject: [PATCH 4/7] update cses-1191 --- solutions/platinum/cses-1191.mdx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/solutions/platinum/cses-1191.mdx b/solutions/platinum/cses-1191.mdx index b17891a2e6..26bf5aead0 100644 --- a/solutions/platinum/cses-1191.mdx +++ b/solutions/platinum/cses-1191.mdx @@ -171,7 +171,6 @@ def check(x: int) -> bool: return False - n, k = map(int, input().split()) a = list(map(int, input().split())) @@ -188,19 +187,19 @@ if k >= total_sum: exit(0) # two pointers to find each subarray range [l, r) -current_sum = 0 +curr_sum = 0 r = 0 for l in range(n): - while current_sum + a[r % n] <= k: - current_sum += a[r % n] + while curr_sum + a[r % n] <= k: + curr_sum += a[r % n] r += 1 # initialize binary lifting arrays nxt[l][0] = r % n length[l][0] = r - l - current_sum -= a[l] + curr_sum -= a[l] for j in range(1, MAXL): for i in range(n): From eedc9322f08d0bc7dfc5b0af6f0601be177410b3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:18:50 +0000 Subject: [PATCH 5/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/cses-1191.mdx | 72 ++++++++++++++++---------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/solutions/platinum/cses-1191.mdx b/solutions/platinum/cses-1191.mdx index 26bf5aead0..8a2de3ef5d 100644 --- a/solutions/platinum/cses-1191.mdx +++ b/solutions/platinum/cses-1191.mdx @@ -148,27 +148,27 @@ length = [[0] * MAXL for _ in range(MAXN)] def len_between(x: int, y: int) -> int: - """ - :returns the number of elements between the subarray - starting at x and the next y subarrays - """ - total = 0 - for i in range(MAXL): - if y & (1 << i): - total += length[x][i] - x = nxt[x][i] - return total + """ + :returns the number of elements between the subarray + starting at x and the next y subarrays + """ + total = 0 + for i in range(MAXL): + if y & (1 << i): + total += length[x][i] + x = nxt[x][i] + return total def check(x: int) -> bool: - """ - :return true if there is a series of x subarrays - that uses the whole array - """ - for i in range(n): - if len_between(i, x) >= n: - return True - return False + """ + :return true if there is a series of x subarrays + that uses the whole array + """ + for i in range(n): + if len_between(i, x) >= n: + return True + return False n, k = map(int, input().split()) @@ -183,40 +183,40 @@ if k == 1: # edge case: every element can go into one subarray if k >= total_sum: - print(1) - exit(0) + print(1) + exit(0) # two pointers to find each subarray range [l, r) curr_sum = 0 r = 0 for l in range(n): - while curr_sum + a[r % n] <= k: - curr_sum += a[r % n] - r += 1 + while curr_sum + a[r % n] <= k: + curr_sum += a[r % n] + r += 1 - # initialize binary lifting arrays - nxt[l][0] = r % n - length[l][0] = r - l + # initialize binary lifting arrays + nxt[l][0] = r % n + length[l][0] = r - l - curr_sum -= a[l] + curr_sum -= a[l] for j in range(1, MAXL): - for i in range(n): - nxt[i][j] = nxt[nxt[i][j - 1]][j - 1] - # add lengths from the left and the right - length[i][j] = length[i][j - 1] + length[nxt[i][j - 1]][j - 1] + for i in range(n): + nxt[i][j] = nxt[nxt[i][j - 1]][j - 1] + # add lengths from the left and the right + length[i][j] = length[i][j - 1] + length[nxt[i][j - 1]][j - 1] # binary search over the least amount of subarrays # needed to cover the whole array lo, hi = 0, n while lo < hi - 1: - mid = (lo + hi) // 2 - if check(mid): - hi = mid - else: - lo = mid + mid = (lo + hi) // 2 + if check(mid): + hi = mid + else: + lo = mid print(hi) ``` From e81a690b05aa744d9d5d739998af94fcc450f03e Mon Sep 17 00:00:00 2001 From: freakin23 Date: Tue, 17 Dec 2024 13:02:48 +0530 Subject: [PATCH 6/7] update Cyclic Array --- solutions/platinum/cses-1191.mdx | 244 ++++++++++--------------------- 1 file changed, 74 insertions(+), 170 deletions(-) diff --git a/solutions/platinum/cses-1191.mdx b/solutions/platinum/cses-1191.mdx index 8a2de3ef5d..4bd409079c 100644 --- a/solutions/platinum/cses-1191.mdx +++ b/solutions/platinum/cses-1191.mdx @@ -2,126 +2,75 @@ id: cses-1191 source: CSES title: Cyclic Array -author: Chongtian Ma +author: Chongtian Ma, Rameez Parwez --- ## Explanation We can turn this into a graph problem, or more precisely, a tree. Let's add an edge from $u$ to $v$ if the maximum length of the subarray that starts at $v$ has to end at $u-1$, and so the next subarray must start at $u$. Remember the array is cyclic, so $u$ is not necessarily greater than $v$. This can be found using basic two pointers. We can iterate through the array with our left pointer and increment our right pointer if the sum between the two pointers is less than or equal to $k$. This will ensure that the subarray starting at the left pointer is as big as possible. -We can use binary lifting to find the start index of any next subarray in $\log(n)$ time. We can also track the number of elements between any two subarrays. - -Finally, we can binary search over the number of subarrays for the minimum. If we can use $x$ subarrays to cover the whole array, then we must also be able to use $x+1$ subarrays. - +We can use binary lifting for each possible starting position to determine the number of jumps required to cover exactly $n$ elements. + ## Implementation -**Time Complexity: $\mathcal O(n \log^2n)$** +**Time Complexity: $\mathcal O(n \log n)$** ```cpp -#include -using namespace std; -using ll = long long; +#include -const int MAXN = 2e5; -const int MAXL = 20; // approx maximum log base 2 of n +const int MAX_N = 2e5 + 1; +const int LOG = 20; /* * next[i][j] stores the start point of the next * 2^j'th subarray if we choose this subarray starting at i */ -array, MAXN> nxt; +int nxt[2 * MAX_N][LOG]; -/* - * len[i][j] stores the total amount of elements between - * subarray starting at i and the next 2^j'th subarray - */ -array, MAXN> len; +int arr[2 * MAX_N]; int main() { - ios::sync_with_stdio(false); - cin.tie(0); - - int n; - ll k; - scanf("%d%lld", &n, &k); - vector a(n); - for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } - - ll sum = 0; - for (int i = 0; i < n; i++) { sum += a[i]; } - - // edge case: every element can go into one subarray - if (k >= sum) { - printf("%d", 1); - return 0; - } - - // two pointers to find each subarray of the range [l,r) - sum = 0; - for (ll l = 0, r = 0; l < n; l++) { - while (sum + a[r % n] <= k) { - sum += a[r % n]; - r++; - } - - // initialize binary lifting arrays - nxt[l][0] = r % n; - len[l][0] = r - l; - - sum -= a[l]; - } - - for (int j = 1; j < MAXL; j++) { - for (int i = 0; i < n; i++) { - nxt[i][j] = nxt[nxt[i][j - 1]][j - 1]; - // add lengths from the left and the right - len[i][j] = len[i][j - 1] + len[nxt[i][j - 1]][j - 1]; - } - } - - /* - * returns the number of elements between the subarray - * starting at x and the next y subarrays - */ - auto len_between = [&](int x, int y) -> ll { - ll total = 0; - for (int i = 0; i < MAXL; i++) { - if (y & (1 << i)) { - total += len[x][i]; - x = nxt[x][i]; - } - } - - return total; - }; - - /* - * return true if there is a series of x subarrays - * that uses the whole array - */ - auto check = [&](int x) -> bool { - for (int i = 0; i < n; i++) { - if (len_between(i, x) >= n) { return true; } - } - return false; - }; - - /* - * binary search over the least amount of subarrays - * needed to cover the whole array - */ - int l = 0, r = n; - while (l < r - 1) { - int mid = l + (r - l) / 2; - if (check(mid)) { - r = mid; - } else l = mid; - } - - printf("%d\n", r); + int n; + long long k; + std::cin >> n >> k; + for (int i = 1; i <= n; i++) { + std::cin >> arr[i]; + arr[i + n] = arr[i]; + } + + for (int i = 1, j = 1; i <= 2 * n; i++) { + arr[i] += arr[i - 1]; + while (arr[i] - arr[j] > k) { + j++; + } + nxt[i][0] = j; // farthest position reachable from i + } + + // binary lifting transition + for (int j = 1; j < LOG; j++) { + for (int i = 1; i <= 2 * n; i++) { + nxt[i][j] = nxt[nxt[i][j - 1]][j - 1]; + } + } + + int res = n; + for (int i = n; i <= 2 * n; i++) { + int curr_res = 1; + int pos = i; + + // using binary lifting to simulate jumps and cover 'n' elements + for (int j = LOG - 1; j >= 0; j--) { + if (nxt[pos][j] > i - n) { + curr_res += (1 << j); + pos = nxt[pos][j]; + } + } + res = std::min(res, curr_res); + } + std::cout << res << '\n'; } ``` @@ -130,95 +79,50 @@ int main() { -Due to Python's constant factor, the below solution TLEs on a couple test cases. +Submit the solution in PyPy3 to avoid TLE. ```py -MAXN = 200000 -MAXL = 20 # approx maximum log base 2 of n +MAX_N = 200001 +LOG = 20 # next[i][j] stores the start point of the next # 2^j'th subarray if we choose this subarray starting at i -nxt = [[0] * MAXL for _ in range(MAXN)] - -# len[i][j] stores the total amount of elements between -# subarray starting at i and the next 2^j'th subarray -length = [[0] * MAXL for _ in range(MAXN)] - - -def len_between(x: int, y: int) -> int: - """ - :returns the number of elements between the subarray - starting at x and the next y subarrays - """ - total = 0 - for i in range(MAXL): - if y & (1 << i): - total += length[x][i] - x = nxt[x][i] - return total - - -def check(x: int) -> bool: - """ - :return true if there is a series of x subarrays - that uses the whole array - """ - for i in range(n): - if len_between(i, x) >= n: - return True - return False - +nxt = [[0] * LOG for _ in range(2 * MAX_N)] n, k = map(int, input().split()) -a = list(map(int, input().split())) - -total_sum = sum(a) - -# edge case: each element create their own subarray -if k == 1: - print(n) - exit(0) - -# edge case: every element can go into one subarray -if k >= total_sum: - print(1) - exit(0) +arr = list(map(int, input().split())) -# two pointers to find each subarray range [l, r) -curr_sum = 0 -r = 0 +arr = [0] + arr + arr -for l in range(n): - while curr_sum + a[r % n] <= k: - curr_sum += a[r % n] - r += 1 +j = 1 +for i in range(1, 2 * n + 1): + arr[i] += arr[i - 1] + while arr[i] - arr[j] > k: + j += 1 - # initialize binary lifting arrays - nxt[l][0] = r % n - length[l][0] = r - l + nxt[i][0] = j # farthest position reachable from i - curr_sum -= a[l] +# binary lifting transition +for j in range(1, LOG): + for i in range(1, 2 * n + 1): + nxt[i][j] = nxt[nxt[i][j - 1]][j - 1] -for j in range(1, MAXL): - for i in range(n): - nxt[i][j] = nxt[nxt[i][j - 1]][j - 1] - # add lengths from the left and the right - length[i][j] = length[i][j - 1] + length[nxt[i][j - 1]][j - 1] +res = n +for i in range(n, 2 * n + 1): + curr_res = 1 + pos = i + # using binary lifting to simulate jumps and cover 'n' elements + for j in range(LOG - 1, -1, -1): + if nxt[pos][j] > i - n: + curr_res += (1 << j) + pos = nxt[pos][j] -# binary search over the least amount of subarrays -# needed to cover the whole array -lo, hi = 0, n -while lo < hi - 1: - mid = (lo + hi) // 2 - if check(mid): - hi = mid - else: - lo = mid + res = min(res, curr_res) -print(hi) +print(res) ``` From 85fcda23006930be0b3ddc34288019ec45b7943b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 07:34:39 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/cses-1191.mdx | 102 +++++++++++++++---------------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/solutions/platinum/cses-1191.mdx b/solutions/platinum/cses-1191.mdx index 4bd409079c..aed27800e2 100644 --- a/solutions/platinum/cses-1191.mdx +++ b/solutions/platinum/cses-1191.mdx @@ -10,7 +10,7 @@ author: Chongtian Ma, Rameez Parwez We can turn this into a graph problem, or more precisely, a tree. Let's add an edge from $u$ to $v$ if the maximum length of the subarray that starts at $v$ has to end at $u-1$, and so the next subarray must start at $u$. Remember the array is cyclic, so $u$ is not necessarily greater than $v$. This can be found using basic two pointers. We can iterate through the array with our left pointer and increment our right pointer if the sum between the two pointers is less than or equal to $k$. This will ensure that the subarray starting at the left pointer is as big as possible. We can use binary lifting for each possible starting position to determine the number of jumps required to cover exactly $n$ elements. - + ## Implementation **Time Complexity: $\mathcal O(n \log n)$** @@ -33,44 +33,40 @@ int nxt[2 * MAX_N][LOG]; int arr[2 * MAX_N]; int main() { - int n; - long long k; - std::cin >> n >> k; - for (int i = 1; i <= n; i++) { - std::cin >> arr[i]; - arr[i + n] = arr[i]; - } - - for (int i = 1, j = 1; i <= 2 * n; i++) { - arr[i] += arr[i - 1]; - while (arr[i] - arr[j] > k) { - j++; - } - nxt[i][0] = j; // farthest position reachable from i - } - - // binary lifting transition - for (int j = 1; j < LOG; j++) { - for (int i = 1; i <= 2 * n; i++) { - nxt[i][j] = nxt[nxt[i][j - 1]][j - 1]; - } - } - - int res = n; - for (int i = n; i <= 2 * n; i++) { - int curr_res = 1; - int pos = i; - - // using binary lifting to simulate jumps and cover 'n' elements - for (int j = LOG - 1; j >= 0; j--) { - if (nxt[pos][j] > i - n) { - curr_res += (1 << j); - pos = nxt[pos][j]; - } - } - res = std::min(res, curr_res); - } - std::cout << res << '\n'; + int n; + long long k; + std::cin >> n >> k; + for (int i = 1; i <= n; i++) { + std::cin >> arr[i]; + arr[i + n] = arr[i]; + } + + for (int i = 1, j = 1; i <= 2 * n; i++) { + arr[i] += arr[i - 1]; + while (arr[i] - arr[j] > k) { j++; } + nxt[i][0] = j; // farthest position reachable from i + } + + // binary lifting transition + for (int j = 1; j < LOG; j++) { + for (int i = 1; i <= 2 * n; i++) { nxt[i][j] = nxt[nxt[i][j - 1]][j - 1]; } + } + + int res = n; + for (int i = n; i <= 2 * n; i++) { + int curr_res = 1; + int pos = i; + + // using binary lifting to simulate jumps and cover 'n' elements + for (int j = LOG - 1; j >= 0; j--) { + if (nxt[pos][j] > i - n) { + curr_res += (1 << j); + pos = nxt[pos][j]; + } + } + res = std::min(res, curr_res); + } + std::cout << res << '\n'; } ``` @@ -98,29 +94,29 @@ arr = [0] + arr + arr j = 1 for i in range(1, 2 * n + 1): - arr[i] += arr[i - 1] - while arr[i] - arr[j] > k: - j += 1 + arr[i] += arr[i - 1] + while arr[i] - arr[j] > k: + j += 1 - nxt[i][0] = j # farthest position reachable from i + nxt[i][0] = j # farthest position reachable from i # binary lifting transition for j in range(1, LOG): - for i in range(1, 2 * n + 1): - nxt[i][j] = nxt[nxt[i][j - 1]][j - 1] + for i in range(1, 2 * n + 1): + nxt[i][j] = nxt[nxt[i][j - 1]][j - 1] res = n for i in range(n, 2 * n + 1): - curr_res = 1 - pos = i + curr_res = 1 + pos = i - # using binary lifting to simulate jumps and cover 'n' elements - for j in range(LOG - 1, -1, -1): - if nxt[pos][j] > i - n: - curr_res += (1 << j) - pos = nxt[pos][j] + # using binary lifting to simulate jumps and cover 'n' elements + for j in range(LOG - 1, -1, -1): + if nxt[pos][j] > i - n: + curr_res += 1 << j + pos = nxt[pos][j] - res = min(res, curr_res) + res = min(res, curr_res) print(res) ```