Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 225 additions & 9 deletions solution/3800-3899/3886.Sum of Sortable Integers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,49 +100,265 @@ tags:

<!-- solution:start -->

### 方法一
### 方法一:枚举 + 模拟

<!-- thinking:start -->

> **思考**
>
> $k$ 为可排序整数当且仅当 $k \mid n$,且每段长 $k$ 的子数组可经循环移位拼成全局非递减。 $n \le 10^5$。
> 若对每个因数的每一段都尝试全部 $k$ 种循环移位,单段比较就是 $O(k^2)$。$n \le 10^5$ 时,这一做法会超出时间限制。
>
> 因数个数少,枚举 $n$ 的因数即可。对每个 $k$,每段必须是某段循环移位,等价于段内相对循环秩序能对接相邻段。
> 开销集中在同一段被反复旋转。一段经循环移位后能变成非递减,当且仅当首尾相接后严格下降至多出现一次。各段都旋成非递减之后,每个分界处还要满足左侧最大值不超过右侧最小值,拼接结果才会全局非递减。
>
> 全局非递减意味着把各段旋转后拼接应单调。可检查每个位置与「排序后应在的循环位置」是否一致,或比较相邻段的最小旋转。
> 下降次数和两侧最值都落在段的边界上。
>
> 对通过检查的因数求和。
> 我们预处理前缀最大值、后缀最小值和相邻下降的前缀和,再枚举 $n$ 的因数,对每一段用 $O(1)$ 时间完成判断。

<!-- thinking:end -->

先求出三个辅助数组。$\textit{prefMax}[i]$ 是 $\textit{nums}[0..i-1]$ 的最大值,$\textit{prefMax}[0] = 0$。$\textit{suffMin}[i]$ 是 $\textit{nums}[i..n-1]$ 的最小值。$\textit{prefDrop}[i]$ 统计下标对 $(0,1),\ldots,(i-1,i)$ 中 $\textit{nums}[j-1] > \textit{nums}[j]$ 的次数。

接着枚举 $n$ 的每个因数 $k$,把数组分成若干长度为 $k$ 的段。对起点 $i$,段内相邻下降次数为 $\textit{prefDrop}[i+k-1] - \textit{prefDrop}[i]$,若 $\textit{nums}[i+k-1] > \textit{nums}[i]$ 再加一次。这个总数不超过 $1$,并且 $\textit{prefMax}[i] \le \textit{suffMin}[i]$ 时,该段可以通过循环移位接到前面的有序部分之后。全部分段都通过,就把 $k$ 加入答案。

时间复杂度 $O(n + \sigma(n))$,空间复杂度 $O(n)$。其中 $n$ 是数组长度,$\sigma(n)$ 是 $n$ 的全部正因数之和。

<!-- tabs:start -->

#### Python3

```python

class Solution:
def sortableIntegers(self, nums: list[int]) -> int:
n = len(nums)
pref_max = [0] * (n + 1)
for i, x in enumerate(nums):
pref_max[i + 1] = max(pref_max[i], x)
suff_min = [10**9] * (n + 1)
for i in range(n - 1, -1, -1):
suff_min[i] = min(nums[i], suff_min[i + 1])
pref_drop = [0] * n
for i in range(1, n):
pref_drop[i] = pref_drop[i - 1] + (nums[i - 1] > nums[i])

def check(k: int) -> bool:
for start in range(0, n, k):
end = start + k - 1
drops = pref_drop[end] - pref_drop[start]
if nums[end] > nums[start]:
drops += 1
if drops > 1 or pref_max[start] > suff_min[start]:
return False
return True

ans = 0
i = 1
while i <= n // i:
if n % i == 0:
if check(i):
ans += i
other = n // i
if i != other and check(other):
ans += other
i += 1
return ans
```

#### Java

```java

class Solution {
public int sortableIntegers(int[] nums) {
int n = nums.length;
int[] prefMax = new int[n + 1];
for (int i = 0; i < n; ++i) {
prefMax[i + 1] = Math.max(prefMax[i], nums[i]);
}
int[] suffMin = new int[n + 1];
suffMin[n] = 1000000000;
for (int i = n - 1; i >= 0; --i) {
suffMin[i] = Math.min(nums[i], suffMin[i + 1]);
}
int[] prefDrop = new int[n];
for (int i = 1; i < n; ++i) {
prefDrop[i] = prefDrop[i - 1] + (nums[i - 1] > nums[i] ? 1 : 0);
}
int ans = 0;
for (int i = 1; i <= n / i; ++i) {
if (n % i == 0) {
if (check(nums, prefMax, suffMin, prefDrop, i)) {
ans += i;
}
if (i != n / i && check(nums, prefMax, suffMin, prefDrop, n / i)) {
ans += n / i;
}
}
}
return ans;
}

private boolean check(int[] nums, int[] prefMax, int[] suffMin, int[] prefDrop, int k) {
int n = nums.length;
for (int start = 0; start < n; start += k) {
int end = start + k - 1;
int drops = prefDrop[end] - prefDrop[start];
if (nums[end] > nums[start]) {
++drops;
}
if (drops > 1 || prefMax[start] > suffMin[start]) {
return false;
}
}
return true;
}
}
```

#### C++

```cpp

class Solution {
public:
int sortableIntegers(vector<int>& nums) {
int n = nums.size();
vector<int> prefMax(n + 1);
for (int i = 0; i < n; ++i) {
prefMax[i + 1] = max(prefMax[i], nums[i]);
}
vector<int> suffMin(n + 1, 1000000000);
for (int i = n - 1; i >= 0; --i) {
suffMin[i] = min(nums[i], suffMin[i + 1]);
}
vector<int> prefDrop(n);
for (int i = 1; i < n; ++i) {
prefDrop[i] = prefDrop[i - 1] + (nums[i - 1] > nums[i]);
}
auto check = [&](int k) {
for (int start = 0; start < n; start += k) {
int end = start + k - 1;
int drops = prefDrop[end] - prefDrop[start];
if (nums[end] > nums[start]) {
++drops;
}
if (drops > 1 || prefMax[start] > suffMin[start]) {
return false;
}
}
return true;
};
int ans = 0;
for (int i = 1; i <= n / i; ++i) {
if (n % i == 0) {
if (check(i)) {
ans += i;
}
if (i != n / i && check(n / i)) {
ans += n / i;
}
}
}
return ans;
}
};
```

#### Go

```go
func sortableIntegers(nums []int) int {
n := len(nums)
prefMax := make([]int, n+1)
for i, x := range nums {
prefMax[i+1] = max(prefMax[i], x)
}
suffMin := make([]int, n+1)
suffMin[n] = 1000000000
for i := n - 1; i >= 0; i-- {
suffMin[i] = min(nums[i], suffMin[i+1])
}
prefDrop := make([]int, n)
for i := 1; i < n; i++ {
prefDrop[i] = prefDrop[i-1]
if nums[i-1] > nums[i] {
prefDrop[i]++
}
}
check := func(k int) bool {
for start := 0; start < n; start += k {
end := start + k - 1
drops := prefDrop[end] - prefDrop[start]
if nums[end] > nums[start] {
drops++
}
if drops > 1 || prefMax[start] > suffMin[start] {
return false
}
}
return true
}
ans := 0
for i := 1; i <= n/i; i++ {
if n%i == 0 {
if check(i) {
ans += i
}
if i != n/i && check(n/i) {
ans += n / i
}
}
}
return ans
}
```

#### TypeScript

```ts
function sortableIntegers(nums: number[]): number {
const n = nums.length;
const prefMax = new Array<number>(n + 1).fill(0);
for (let i = 0; i < n; i++) {
prefMax[i + 1] = Math.max(prefMax[i], nums[i]);
}
const suffMin = new Array<number>(n + 1).fill(1e9);
for (let i = n - 1; i >= 0; i--) {
suffMin[i] = Math.min(nums[i], suffMin[i + 1]);
}
const prefDrop = new Array<number>(n).fill(0);
for (let i = 1; i < n; i++) {
prefDrop[i] = prefDrop[i - 1] + (nums[i - 1] > nums[i] ? 1 : 0);
}

const check = (k: number): boolean => {
for (let start = 0; start < n; start += k) {
const end = start + k - 1;
let drops = prefDrop[end] - prefDrop[start];
if (nums[end] > nums[start]) {
drops++;
}
if (drops > 1 || prefMax[start] > suffMin[start]) {
return false;
}
}
return true;
};

let ans = 0;
for (let i = 1; i <= Math.floor(n / i); i++) {
if (n % i === 0) {
const other = n / i;
if (check(i)) {
ans += i;
}
if (i !== other && check(other)) {
ans += other;
}
}
}
return ans;
}
```

<!-- tabs:end -->

<!-- solution:end -->

<!-- problem:end -->
Loading
Loading