diff --git a/solutions/platinum/cses-1191.mdx b/solutions/platinum/cses-1191.mdx index 2945f85f45..aed27800e2 100644 --- a/solutions/platinum/cses-1191.mdx +++ b/solutions/platinum/cses-1191.mdx @@ -2,127 +2,124 @@ 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; + long long k; + std::cin >> n >> k; + for (int i = 1; i <= n; i++) { + std::cin >> arr[i]; + arr[i + n] = arr[i]; } - // 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 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 } - 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]; - } + // 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]; } } - /* - * 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]; - } - } + int res = n; + for (int i = n; i <= 2 * n; i++) { + int curr_res = 1; + int pos = 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; } + // 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]; + } } - 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; + res = std::min(res, curr_res); } - - printf("%d\n", r); + std::cout << res << '\n'; } ``` + + + + + +Submit the solution in PyPy3 to avoid TLE. + + + +```py +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] * LOG for _ in range(2 * MAX_N)] + +n, k = map(int, input().split()) +arr = list(map(int, input().split())) + +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 + + 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] + +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] + + res = min(res, curr_res) + +print(res) +``` + +