Skip to content

Commit

Permalink
Merge pull request #4985 from Sosuke23/a
Browse files Browse the repository at this point in the history
update Cyclic Array
  • Loading branch information
ryanchou-dev authored Dec 23, 2024
2 parents ccc19cf + 85fcda2 commit d322bfc
Showing 1 changed file with 84 additions and 87 deletions.
171 changes: 84 additions & 87 deletions solutions/platinum/cses-1191.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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)$**

<LanguageSection>
<CPPSection>

```cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#include <iostream>

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<array<int, MAXL>, 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<array<ll, MAXL>, 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<int> 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';
}
```

</CPPSection>
<PySection>

<Warning>

Submit the solution in PyPy3 to avoid TLE.

</Warning>

```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)
```

</PySection>
</LanguageSection>

0 comments on commit d322bfc

Please sign in to comment.