diff --git a/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/README.md b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/README.md index 6f9505c51dede..aca1f22bff209 100644 --- a/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/README.md +++ b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/README.md @@ -105,32 +105,274 @@ source: 第 499 场周赛 Q4 -### 方法一 +### 方法一:动态规划 + 树状数组 + +**状态定义** + +定义 $f[i][0]$ 表示以下标 $i$ 结尾、且末位元素为**谷**(需要下一个更大才符合交替)的合法子序列最大和;$f[i][1]$ 表示以下标 $i$ 结尾、且末位元素为**峰**(需要下一个更小才符合交替)的合法子序列最大和。 + +**状态转移** + +转移时枚举满足 $j \leq i - k$ 的前驱下标 $j$: + +- 状态 $f[i][0]$(谷):由 $f[j][1]$ 转移,需要 $\text{nums}[j] > \text{nums}[i]$,即查询值域 $(\text{nums}[i],\ +\infty)$ 上 $f[\cdot][1]$ 的最大值: + +$$f[i][0] = \text{nums}[i] + \max\!\left(0,\ \max_{\substack{j \leq i-k \\ \text{nums}[j] > \text{nums}[i]}} f[j][1]\right)$$ + +- 状态 $f[i][1]$(峰):由 $f[j][0]$ 转移,需要 $\text{nums}[j] < \text{nums}[i]$,即查询值域 $[1,\ \text{nums}[i]-1]$ 上 $f[\cdot][0]$ 的最大值: + +$$f[i][1] = \text{nums}[i] + \max\!\left(0,\ \max_{\substack{j \leq i-k \\ \text{nums}[j] < \text{nums}[i]}} f[j][0]\right)$$ + +最终答案为 $\max_{0 \leq i < n}\max(f[i][0],\ f[i][1])$。 + +**优化** + +上述转移涉及动态的值域前缀/后缀最大值查询,可以用两棵**树状数组**维护: + +- 树状数组 $\text{bit}_0$:以值为下标,维护 $f[\cdot][0]$ 的前缀最大值,用于查询 $\text{nums}[j] < \text{nums}[i]$ 的情况。 +- 树状数组 $\text{bit}_1$:以 $M + 1 - \text{val}$(其中 $M = \max(\text{nums})$)为倒置下标,维护 $f[\cdot][1]$ 的前缀最大值,等价于值域上的后缀最大值,用于查询 $\text{nums}[j] > \text{nums}[i]$ 的情况。 + +为了保证只有 $j \leq i - k$ 的下标才能参与转移,在处理第 $i$ 个元素时,将第 $i - k$ 个元素的状态加入树状数组。 + +时间复杂度 $O(n \log M)$,空间复杂度 $O(M)$,其中 $n$ 为数组长度,而 $M = \max(\text{nums})$。 #### Python3 ```python - +class FenwickTree: + def __init__(self, n): + self.n = n + self.tree = [0]*(n+1) + + def update(self, index: int, val: int) -> None: + while index <= self.n: + self.tree[index] = max(self.tree[index], val) + index += (index&(-index)) # 往后更新 + + def preSum(self, pos): + # 按照预期的方式求前缀最大值 + ans = 0 + while pos >= 1: + ans = max(ans, self.tree[pos]) + pos -= (pos&(-pos)) + return ans + +class Solution: + def maxAlternatingSum(self, nums: list[int], k: int) -> int: + stl = sorted(set(nums)) # 将nums中不同的数字进行排序 + rank = {v:i+1 for i,v in enumerate(stl)} # 将nums中的值快速转换成stl中的索引 + fwt0 = FenwickTree(len(stl)) + fwt1 = FenwickTree(len(stl)) + + n = len(nums) + dp = [[0,0] for _ in range(n)] + res = nums[0] + for i in range(n): + dp[i][0] = dp[i][1] = nums[i] + if i >= k: + indx = rank[nums[i]] # 找到nums[i]在stl中的索引 + dp[i][1] = max(dp[i][1], fwt0.preSum(indx-1)+nums[i]) # indx-1即表示小于nums[i]的部分 + dp[i][0] = max(dp[i][0], fwt1.preSum(len(stl)-indx)+nums[i]) # len(stl)-indx即表示在倒序列表中大于nums[i]的部分 + + if i-k+1 >= 0: + indx = rank[nums[i-k+1]] + fwt0.update(indx, dp[i-k+1][0]) # 在正序列表中更新i-k+1位置的值 + fwt1.update(len(stl)-indx+1, dp[i-k+1][1]) # 在倒序列表中更新i-k+1位置的值 + + res = max(res, dp[i][0], dp[i][1]) # 更新答案 + + return res ``` #### Java ```java - +class Solution { + public long maxAlternatingSum(int[] nums, int k) { + long maxSum = 0; + int n = nums.length; + int m = Arrays.stream(nums).max().getAsInt(); + long[][] dp = new long[n][2]; + SegmentTree[] sts = new SegmentTree[2]; + for (int j = 0; j < 2; j++) { + sts[j] = new SegmentTree(m + 1); + } + for (int i = 0; i < n; i++) { + if (i >= k) { + sts[0].update(nums[i - k], dp[i - k][0]); + sts[1].update(nums[i - k], dp[i - k][1]); + } + dp[i][0] = sts[1].getMax(0, nums[i] - 1) + nums[i]; + dp[i][1] = sts[0].getMax(nums[i] + 1, m) + nums[i]; + maxSum = Math.max(maxSum, Math.max(dp[i][0], dp[i][1])); + } + return maxSum; + } +} + +class SegmentTree { + private int n; + private long[] tree; + + public SegmentTree(int n) { + this.n = n; + this.tree = new long[n * 4]; + } + + public long getMax(int start, int end) { + return getMax(start, end, 0, 0, n - 1); + } + + public void update(int index, long value) { + update(index, value, 0, 0, n - 1); + } + + private long getMax(int rangeStart, int rangeEnd, int treeIndex, int treeStart, int treeEnd) { + if (rangeStart > rangeEnd) { + return 0; + } + if (rangeStart == treeStart && rangeEnd == treeEnd) { + return tree[treeIndex]; + } + int mid = treeStart + (treeEnd - treeStart) / 2; + if (rangeEnd <= mid) { + return getMax(rangeStart, rangeEnd, treeIndex * 2 + 1, treeStart, mid); + } else if (rangeStart > mid) { + return getMax(rangeStart, rangeEnd, treeIndex * 2 + 2, mid + 1, treeEnd); + } else { + return Math.max(getMax(rangeStart, mid, treeIndex * 2 + 1, treeStart, mid), getMax(mid + 1, rangeEnd, treeIndex * 2 + 2, mid + 1, treeEnd)); + } + } + + private void update(int rangeIndex, long value, int treeIndex, int start, int end) { + if (start == end) { + tree[treeIndex] = value; + return; + } + int mid = start + (end - start) / 2; + if (rangeIndex <= mid) { + update(rangeIndex, value, treeIndex * 2 + 1, start, mid); + } else { + update(rangeIndex, value, treeIndex * 2 + 2, mid + 1, end); + } + tree[treeIndex] = Math.max(tree[treeIndex * 2 + 1], tree[treeIndex * 2 + 2]); + } +} ``` #### C++ ```cpp - +class Solution { +public: + long long maxAlternatingSum(vector& nums, int K) { + int n = nums.size(); + + // 离散化 + int idx[n]; + map mp; + for (int x : nums) mp[x] = 1; + int m = 0; + for (auto &p : mp) p.second = ++m; + for (int i = 0; i < n; i++) idx[i] = mp[nums[i]]; + + const long long INF = 1e18; + // tree[0]:前缀最大值(用于查询 < nums[i] 的最大 f[j][0]) + // tree[1]:后缀最大值(用于查询 > nums[i] 的最大 f[j][1]) + long long tree[2][m + 1]; + for (int i = 0; i < 2; i++) for (int j = 0; j <= m; j++) tree[i][j] = -INF; + + // 树状数组模板开始 + + auto lb = [&](int x) { return x & (-x); }; + + auto update = [&](long long *tree, int pos, long long val) { + for (; pos <= m; pos += lb(pos)) tree[pos] = max(tree[pos], val); + }; + + auto query = [&](long long *tree, int pos) { + long long ret = -INF; + for (; pos; pos -= lb(pos)) ret = max(ret, tree[pos]); + return ret; + }; + + // 树状数组模板结束 + + long long ans = 0; + long long f[n + 1][2]; + for (int i = 0; i <= n; i++) for (int j = 0; j < 2; j++) f[i][j] = -INF; + // 滑动窗口:只有 j <= i - K 的位置才加入树状数组 + for (int i = 1, j = 1; i <= n; i++) { + while (i - j >= K) { + update(tree[0], idx[j - 1], f[j][0]); + update(tree[1], m + 1 - idx[j - 1], f[j][1]); + j++; + } + // 谷:从 tree[1] 查询值 > nums[i] 的最大 f[j][1] + f[i][0] = max(0LL, query(tree[1], m - idx[i - 1])) + nums[i - 1]; + // 峰:从 tree[0] 查询值 < nums[i] 的最大 f[j][0] + f[i][1] = max(0LL, query(tree[0], idx[i - 1] - 1)) + nums[i - 1]; + ans = max({ans, f[i][0], f[i][1]}); + } + return ans; + } +}; ``` #### Go ```go - +type fenwick []int64 + +func (f fenwick) update(i int, val int64) { + for ; i < len(f); i += i & -i { + f[i] = max(f[i], val) + } +} + +// [1, i] 中的最大值 +func (f fenwick) preMax(i int) (res int64) { + for ; i > 0; i &= i - 1 { + res = max(res, f[i]) + } + return +} + +func maxAlternatingSum(nums []int, k int) (ans int64) { + // 离散化 nums + sorted := slices.Clone(nums) + slices.Sort(sorted) + sorted = slices.Compact(sorted) + + n := len(nums) + fInc := make([]int64, n) // fInc[i] 表示以 nums[i] 结尾且最后两项递增的交替子序列的最大和 + fDec := make([]int64, n) // fDec[i] 表示以 nums[i] 结尾且最后两项递减的交替子序列的最大和 + + // 值域树状数组 + m := len(sorted) + inc := make(fenwick, m+1) // 维护 fInc[i] 的最大值 + dec := make(fenwick, m+1) // 维护 fDec[i] 的最大值 + + for i, x := range nums { + if i >= k { + // 在这个时候才把 fInc[i-k] 和 fDec[i-k] 添加到值域树状数组中,从而保证转移来源的下标 <= i-k + j := nums[i-k] + inc.update(m-j, fInc[i-k]) // m-j 可以把后缀变成前缀 + dec.update(j+1, fDec[i-k]) + } + + j := sort.SearchInts(sorted, x) + nums[i] = j // 注意这里修改了 nums[i],这样上面的 nums[i-k] 无需二分 + + fInc[i] = dec.preMax(j) + int64(x) // 计算满足 nums[i'] < x 的 fDec[i'] 的最大值 + fDec[i] = inc.preMax(m-1-j) + int64(x) // 计算满足 nums[i'] > x 的 fInc[i'] 的最大值 + ans = max(ans, fInc[i], fDec[i]) // 枚举子序列以 nums[i] 结尾 + } + + return +} ``` diff --git a/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/README_EN.md b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/README_EN.md index 8ed2e8e30fd66..fc5e3ad4cd916 100644 --- a/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/README_EN.md +++ b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/README_EN.md @@ -100,32 +100,261 @@ source: Weekly Contest 499 Q4 -### Solution 1 +### Solution 1: Dynamic Programming + Binary Indexed Tree + +**State Definition** + +Let $f[i][0]$ denote the maximum sum of a valid subsequence ending at index $i$ where the last element is a **valley** (the next element must be larger to maintain alternation), and $f[i][1]$ denote the maximum sum where the last element is a **peak** (the next element must be smaller). + +**Transitions** + +When transitioning, we enumerate a predecessor index $j$ satisfying $j \leq i - k$: + +- State $f[i][0]$ (valley): transitions from $f[j][1]$, requiring $\text{nums}[j] > \text{nums}[i]$, i.e., query the maximum $f[\cdot][1]$ over the value range $(\text{nums}[i],\ +\infty)$: + +$$f[i][0] = \text{nums}[i] + \max\!\left(0,\ \max_{\substack{j \leq i-k \\ \text{nums}[j] > \text{nums}[i]}} f[j][1]\right)$$ + +- State $f[i][1]$ (peak): transitions from $f[j][0]$, requiring $\text{nums}[j] < \text{nums}[i]$, i.e., query the maximum $f[\cdot][0]$ over the value range $[1,\ \text{nums}[i]-1]$: + +$$f[i][1] = \text{nums}[i] + \max\!\left(0,\ \max_{\substack{j \leq i-k \\ \text{nums}[j] < \text{nums}[i]}} f[j][0]\right)$$ + +The final answer is $\max_{0 \leq i < n}\max(f[i][0],\ f[i][1])$. + +**Optimization** + +The transitions involve dynamic prefix/suffix maximum queries over a value domain, which can be maintained efficiently with two **Binary Indexed Trees (BITs)**: + +- BIT $\text{bit}_0$: indexed by value, maintains the prefix maximum of $f[\cdot][0]$, used to query cases where $\text{nums}[j] < \text{nums}[i]$. +- BIT $\text{bit}_1$: indexed by $M + 1 - \text{val}$ (reversed, where $M = \max(\text{nums})$ ), maintains the prefix maximum of $f[\cdot][1]$, equivalent to a suffix maximum over the value domain, used to query cases where $\text{nums}[j] > \text{nums}[i]$. + +To ensure only indices $j \leq i - k$ participate in transitions, when processing index $i$, we insert the state of index $i - k$ into the BITs using a sliding pointer. + +The time complexity is $O(n \log M)$ and the space complexity is $O(M)$, where $n$ is the length of the array and $M = \max(\text{nums})$. #### Python3 ```python - +class FenwickTree: + def __init__(self, n): + self.n = n + self.tree = [0]*(n+1) + + def update(self, index: int, val: int) -> None: + while index <= self.n: + self.tree[index] = max(self.tree[index], val) + index += (index&(-index)) + + def preSum(self, pos): + ans = 0 + while pos >= 1: + ans = max(ans, self.tree[pos]) + pos -= (pos&(-pos)) + return ans + +class Solution: + def maxAlternatingSum(self, nums: list[int], k: int) -> int: + stl = sorted(set(nums)) + rank = {v:i+1 for i,v in enumerate(stl)} + fwt0 = FenwickTree(len(stl)) + fwt1 = FenwickTree(len(stl)) + + n = len(nums) + dp = [[0,0] for _ in range(n)] + res = nums[0] + for i in range(n): + dp[i][0] = dp[i][1] = nums[i] + if i >= k: + indx = rank[nums[i]] + dp[i][1] = max(dp[i][1], fwt0.preSum(indx-1)+nums[i]) + dp[i][0] = max(dp[i][0], fwt1.preSum(len(stl)-indx)+nums[i]) + + if i-k+1 >= 0: + indx = rank[nums[i-k+1]] + fwt0.update(indx, dp[i-k+1][0]) + fwt1.update(len(stl)-indx+1, dp[i-k+1][1]) + + res = max(res, dp[i][0], dp[i][1]) + + return res ``` #### Java ```java - +class Solution { + public long maxAlternatingSum(int[] nums, int k) { + long maxSum = 0; + int n = nums.length; + int m = Arrays.stream(nums).max().getAsInt(); + long[][] dp = new long[n][2]; + SegmentTree[] sts = new SegmentTree[2]; + for (int j = 0; j < 2; j++) { + sts[j] = new SegmentTree(m + 1); + } + for (int i = 0; i < n; i++) { + if (i >= k) { + sts[0].update(nums[i - k], dp[i - k][0]); + sts[1].update(nums[i - k], dp[i - k][1]); + } + dp[i][0] = sts[1].getMax(0, nums[i] - 1) + nums[i]; + dp[i][1] = sts[0].getMax(nums[i] + 1, m) + nums[i]; + maxSum = Math.max(maxSum, Math.max(dp[i][0], dp[i][1])); + } + return maxSum; + } +} + +class SegmentTree { + private int n; + private long[] tree; + + public SegmentTree(int n) { + this.n = n; + this.tree = new long[n * 4]; + } + + public long getMax(int start, int end) { + return getMax(start, end, 0, 0, n - 1); + } + + public void update(int index, long value) { + update(index, value, 0, 0, n - 1); + } + + private long getMax(int rangeStart, int rangeEnd, int treeIndex, int treeStart, int treeEnd) { + if (rangeStart > rangeEnd) { + return 0; + } + if (rangeStart == treeStart && rangeEnd == treeEnd) { + return tree[treeIndex]; + } + int mid = treeStart + (treeEnd - treeStart) / 2; + if (rangeEnd <= mid) { + return getMax(rangeStart, rangeEnd, treeIndex * 2 + 1, treeStart, mid); + } else if (rangeStart > mid) { + return getMax(rangeStart, rangeEnd, treeIndex * 2 + 2, mid + 1, treeEnd); + } else { + return Math.max(getMax(rangeStart, mid, treeIndex * 2 + 1, treeStart, mid), getMax(mid + 1, rangeEnd, treeIndex * 2 + 2, mid + 1, treeEnd)); + } + } + + private void update(int rangeIndex, long value, int treeIndex, int start, int end) { + if (start == end) { + tree[treeIndex] = value; + return; + } + int mid = start + (end - start) / 2; + if (rangeIndex <= mid) { + update(rangeIndex, value, treeIndex * 2 + 1, start, mid); + } else { + update(rangeIndex, value, treeIndex * 2 + 2, mid + 1, end); + } + tree[treeIndex] = Math.max(tree[treeIndex * 2 + 1], tree[treeIndex * 2 + 2]); + } +} ``` #### C++ ```cpp - +class Solution { +public: + long long maxAlternatingSum(vector& nums, int K) { + int n = nums.size(); + + int idx[n]; + map mp; + for (int x : nums) mp[x] = 1; + int m = 0; + for (auto &p : mp) p.second = ++m; + for (int i = 0; i < n; i++) idx[i] = mp[nums[i]]; + + const long long INF = 1e18; + long long tree[2][m + 1]; + for (int i = 0; i < 2; i++) for (int j = 0; j <= m; j++) tree[i][j] = -INF; + + + auto lb = [&](int x) { return x & (-x); }; + + auto update = [&](long long *tree, int pos, long long val) { + for (; pos <= m; pos += lb(pos)) tree[pos] = max(tree[pos], val); + }; + + auto query = [&](long long *tree, int pos) { + long long ret = -INF; + for (; pos; pos -= lb(pos)) ret = max(ret, tree[pos]); + return ret; + }; + + + long long ans = 0; + long long f[n + 1][2]; + for (int i = 0; i <= n; i++) for (int j = 0; j < 2; j++) f[i][j] = -INF; + for (int i = 1, j = 1; i <= n; i++) { + while (i - j >= K) { + update(tree[0], idx[j - 1], f[j][0]); + update(tree[1], m + 1 - idx[j - 1], f[j][1]); + j++; + } + f[i][0] = max(0LL, query(tree[1], m - idx[i - 1])) + nums[i - 1]; + f[i][1] = max(0LL, query(tree[0], idx[i - 1] - 1)) + nums[i - 1]; + ans = max({ans, f[i][0], f[i][1]}); + } + return ans; + } +}; ``` #### Go ```go - +type fenwick []int64 + +func (f fenwick) update(i int, val int64) { + for ; i < len(f); i += i & -i { + f[i] = max(f[i], val) + } +} + +func (f fenwick) preMax(i int) (res int64) { + for ; i > 0; i &= i - 1 { + res = max(res, f[i]) + } + return +} + +func maxAlternatingSum(nums []int, k int) (ans int64) { + sorted := slices.Clone(nums) + slices.Sort(sorted) + sorted = slices.Compact(sorted) + + n := len(nums) + fInc := make([]int64, n) + fDec := make([]int64, n) + + m := len(sorted) + inc := make(fenwick, m+1) + dec := make(fenwick, m+1) + + for i, x := range nums { + if i >= k { + j := nums[i-k] + inc.update(m-j, fInc[i-k]) + dec.update(j+1, fDec[i-k]) + } + + j := sort.SearchInts(sorted, x) + nums[i] = j + + fInc[i] = dec.preMax(j) + int64(x) + fDec[i] = inc.preMax(m-1-j) + int64(x) + ans = max(ans, fInc[i], fDec[i]) + } + + return +} ``` diff --git a/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.cpp b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.cpp new file mode 100644 index 0000000000000..b3b3268ebc7be --- /dev/null +++ b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.cpp @@ -0,0 +1,54 @@ +class Solution { +public: + long long maxAlternatingSum(vector& nums, int K) { + int n = nums.size(); + + // 离散化 + int idx[n]; + map mp; + for (int x : nums) mp[x] = 1; + int m = 0; + for (auto &p : mp) p.second = ++m; + for (int i = 0; i < n; i++) idx[i] = mp[nums[i]]; + + const long long INF = 1e18; + // tree[0]:前缀最大值(用于查询 < nums[i] 的最大 f[j][0]) + // tree[1]:后缀最大值(用于查询 > nums[i] 的最大 f[j][1]) + long long tree[2][m + 1]; + for (int i = 0; i < 2; i++) for (int j = 0; j <= m; j++) tree[i][j] = -INF; + + // 树状数组模板开始 + + auto lb = [&](int x) { return x & (-x); }; + + auto update = [&](long long *tree, int pos, long long val) { + for (; pos <= m; pos += lb(pos)) tree[pos] = max(tree[pos], val); + }; + + auto query = [&](long long *tree, int pos) { + long long ret = -INF; + for (; pos; pos -= lb(pos)) ret = max(ret, tree[pos]); + return ret; + }; + + // 树状数组模板结束 + + long long ans = 0; + long long f[n + 1][2]; + for (int i = 0; i <= n; i++) for (int j = 0; j < 2; j++) f[i][j] = -INF; + // 滑动窗口:只有 j <= i - K 的位置才加入树状数组 + for (int i = 1, j = 1; i <= n; i++) { + while (i - j >= K) { + update(tree[0], idx[j - 1], f[j][0]); + update(tree[1], m + 1 - idx[j - 1], f[j][1]); + j++; + } + // 谷:从 tree[1] 查询值 > nums[i] 的最大 f[j][1] + f[i][0] = max(0LL, query(tree[1], m - idx[i - 1])) + nums[i - 1]; + // 峰:从 tree[0] 查询值 < nums[i] 的最大 f[j][0] + f[i][1] = max(0LL, query(tree[0], idx[i - 1] - 1)) + nums[i - 1]; + ans = max({ans, f[i][0], f[i][1]}); + } + return ans; + } +}; \ No newline at end of file diff --git a/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.go b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.go new file mode 100644 index 0000000000000..d53a1fd815c3d --- /dev/null +++ b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.go @@ -0,0 +1,49 @@ +type fenwick []int64 + +func (f fenwick) update(i int, val int64) { + for ; i < len(f); i += i & -i { + f[i] = max(f[i], val) + } +} + +// [1, i] 中的最大值 +func (f fenwick) preMax(i int) (res int64) { + for ; i > 0; i &= i - 1 { + res = max(res, f[i]) + } + return +} + +func maxAlternatingSum(nums []int, k int) (ans int64) { + // 离散化 nums + sorted := slices.Clone(nums) + slices.Sort(sorted) + sorted = slices.Compact(sorted) + + n := len(nums) + fInc := make([]int64, n) // fInc[i] 表示以 nums[i] 结尾且最后两项递增的交替子序列的最大和 + fDec := make([]int64, n) // fDec[i] 表示以 nums[i] 结尾且最后两项递减的交替子序列的最大和 + + // 值域树状数组 + m := len(sorted) + inc := make(fenwick, m+1) // 维护 fInc[i] 的最大值 + dec := make(fenwick, m+1) // 维护 fDec[i] 的最大值 + + for i, x := range nums { + if i >= k { + // 在这个时候才把 fInc[i-k] 和 fDec[i-k] 添加到值域树状数组中,从而保证转移来源的下标 <= i-k + j := nums[i-k] + inc.update(m-j, fInc[i-k]) // m-j 可以把后缀变成前缀 + dec.update(j+1, fDec[i-k]) + } + + j := sort.SearchInts(sorted, x) + nums[i] = j // 注意这里修改了 nums[i],这样上面的 nums[i-k] 无需二分 + + fInc[i] = dec.preMax(j) + int64(x) // 计算满足 nums[i'] < x 的 fDec[i'] 的最大值 + fDec[i] = inc.preMax(m-1-j) + int64(x) // 计算满足 nums[i'] > x 的 fInc[i'] 的最大值 + ans = max(ans, fInc[i], fDec[i]) // 枚举子序列以 nums[i] 结尾 + } + + return +} diff --git a/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.java b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.java new file mode 100644 index 0000000000000..c1cd3678fa18b --- /dev/null +++ b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.java @@ -0,0 +1,71 @@ +class Solution { + public long maxAlternatingSum(int[] nums, int k) { + long maxSum = 0; + int n = nums.length; + int m = Arrays.stream(nums).max().getAsInt(); + long[][] dp = new long[n][2]; + SegmentTree[] sts = new SegmentTree[2]; + for (int j = 0; j < 2; j++) { + sts[j] = new SegmentTree(m + 1); + } + for (int i = 0; i < n; i++) { + if (i >= k) { + sts[0].update(nums[i - k], dp[i - k][0]); + sts[1].update(nums[i - k], dp[i - k][1]); + } + dp[i][0] = sts[1].getMax(0, nums[i] - 1) + nums[i]; + dp[i][1] = sts[0].getMax(nums[i] + 1, m) + nums[i]; + maxSum = Math.max(maxSum, Math.max(dp[i][0], dp[i][1])); + } + return maxSum; + } +} + +class SegmentTree { + private int n; + private long[] tree; + + public SegmentTree(int n) { + this.n = n; + this.tree = new long[n * 4]; + } + + public long getMax(int start, int end) { + return getMax(start, end, 0, 0, n - 1); + } + + public void update(int index, long value) { + update(index, value, 0, 0, n - 1); + } + + private long getMax(int rangeStart, int rangeEnd, int treeIndex, int treeStart, int treeEnd) { + if (rangeStart > rangeEnd) { + return 0; + } + if (rangeStart == treeStart && rangeEnd == treeEnd) { + return tree[treeIndex]; + } + int mid = treeStart + (treeEnd - treeStart) / 2; + if (rangeEnd <= mid) { + return getMax(rangeStart, rangeEnd, treeIndex * 2 + 1, treeStart, mid); + } else if (rangeStart > mid) { + return getMax(rangeStart, rangeEnd, treeIndex * 2 + 2, mid + 1, treeEnd); + } else { + return Math.max(getMax(rangeStart, mid, treeIndex * 2 + 1, treeStart, mid), getMax(mid + 1, rangeEnd, treeIndex * 2 + 2, mid + 1, treeEnd)); + } + } + + private void update(int rangeIndex, long value, int treeIndex, int start, int end) { + if (start == end) { + tree[treeIndex] = value; + return; + } + int mid = start + (end - start) / 2; + if (rangeIndex <= mid) { + update(rangeIndex, value, treeIndex * 2 + 1, start, mid); + } else { + update(rangeIndex, value, treeIndex * 2 + 2, mid + 1, end); + } + tree[treeIndex] = Math.max(tree[treeIndex * 2 + 1], tree[treeIndex * 2 + 2]); + } +} \ No newline at end of file diff --git a/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.py b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.py new file mode 100644 index 0000000000000..9e2916e28b787 --- /dev/null +++ b/solution/3900-3999/3915.Maximum Sum of Alternating Subsequence With Distance at Least K/Solution.py @@ -0,0 +1,43 @@ +class FenwickTree: + def __init__(self, n): + self.n = n + self.tree = [0]*(n+1) + + def update(self, index: int, val: int) -> None: + while index <= self.n: + self.tree[index] = max(self.tree[index], val) + index += (index&(-index)) # 往后更新 + + def preSum(self, pos): + # 按照预期的方式求前缀最大值 + ans = 0 + while pos >= 1: + ans = max(ans, self.tree[pos]) + pos -= (pos&(-pos)) + return ans + +class Solution: + def maxAlternatingSum(self, nums: list[int], k: int) -> int: + stl = sorted(set(nums)) # 将nums中不同的数字进行排序 + rank = {v:i+1 for i,v in enumerate(stl)} # 将nums中的值快速转换成stl中的索引 + fwt0 = FenwickTree(len(stl)) + fwt1 = FenwickTree(len(stl)) + + n = len(nums) + dp = [[0,0] for _ in range(n)] + res = nums[0] + for i in range(n): + dp[i][0] = dp[i][1] = nums[i] + if i >= k: + indx = rank[nums[i]] # 找到nums[i]在stl中的索引 + dp[i][1] = max(dp[i][1], fwt0.preSum(indx-1)+nums[i]) # indx-1即表示小于nums[i]的部分 + dp[i][0] = max(dp[i][0], fwt1.preSum(len(stl)-indx)+nums[i]) # len(stl)-indx即表示在倒序列表中大于nums[i]的部分 + + if i-k+1 >= 0: + indx = rank[nums[i-k+1]] + fwt0.update(indx, dp[i-k+1][0]) # 在正序列表中更新i-k+1位置的值 + fwt1.update(len(stl)-indx+1, dp[i-k+1][1]) # 在倒序列表中更新i-k+1位置的值 + + res = max(res, dp[i][0], dp[i][1]) # 更新答案 + + return res \ No newline at end of file