📜  门| GATE-CS-2017(套装2)|问题 14(1)

📅  最后修改于: 2023-12-03 14:58:32.322000             🧑  作者: Mango

题目14

本题涉及对于整数数组的计算。

问题描述

给出一个长度为 $n$ 的整数数组 $A$, 以及两个整数 $l$ 和 $r$。 编写一个程序以确定 $A$ 中所有子数组 $A[i:j]$ 的和之和,并返回结果对 $10^9+7$ 取余数的结果。

举个例子,如果 $A=[1, 2, 3]$, $l=1$, $r=2$, 则可能的子区域为 $[1], [2], [3], [1, 2], [2, 3]$ and $[1, 2, 3]$, 其中 $[1, 2]$ and $[2, 3]$ 的和均在区间 $[l, r]$ 中。

输入格式:

  • 第一行输入整数 $n$,表示整数数组的长度 $(1\leq n\leq 10^5)$;
  • 第二行输入 $n$ 个整数,分别表示 $A[1],A[2],\cdots,A[n]$ $(0\leq A[i]\leq 10^9)$ ;
  • 第三行输入两个整数 $l$ 和 $r$ $(1\leq l\leq r\leq n)$。
示例

输入

3
1 2 3
1 2

输出

6

解释

可能的子区域为 $[1], [2], [3], [1,2], [2,3]$ 和 $[1,2,3]$,其中 $[1,2]$ 和 $[2,3]$ 的和均在区间 $[1,2]$ 中,总和为 $3 + 5 = 8$,对应的答案为 $8%1000000007=8$。

解题思路

这道题目实际上要求的是数组 $A$ 的所有子区间中和在 $[l,r]$ 之间的子区间和的和。对于这道题目,我们可以计算出所有子区间的和,再从中选取和在 $[l,r]$ 之间的子区间和,最后对结果取模。

因此,我们需要计算出所有子区间的和。具体来说,对于 $[i,j]$ 这个区间,其和为 $\sum_{k=i}^j A[k]$。

我们可以使用两重循环来计算出所有的子区间和:

sums = []
for i in range(n):
    for j in range(i,n):
        sub_array = A[i:j+1]
        sub_sum = sum(sub_array)
        sums.append(sub_sum)

其中,sums 列表用于记录所有的子区间和。由于子区间可能非常多,因此时间复杂度为 $O(n^3)$,无法通过本题。

现在的关键在于如何优化这个算法。注意到每一个子区间是由相邻的子区间转移而来的。例如,对于一个数组 $A=[1,2,3,4]$,其子区间包括:

[1]
[2]
[3]
[4]
[1,2]
[2,3]
[3,4]
[1,2,3]
[2,3,4]
[1,2,3,4]

我们可以观察到,左侧的其中前4个子区间,是由左侧第一个子区间与右侧第一个元素组合而来的。后5个子区间中,左侧的其中前3个子区间,是由第5个子区间和右侧第二个元素组合而来的。再往右推,就可以看出所有子区间都是由相邻的子区间转移而来的。

因此,这个算法的优化方向就很明显了:计算出所有长度为 $k$ 的子区间的和,再使用这个和去计算长度为 $k+1$ 的子区间的和。直到计算出所有长度为 $n$ 的子区间的和为止。

具体来说,我们可以通过计算前缀和 $\sum_{i=1}^j A[i]$ 去计算出任意子区间 $[i,j]$ 的和。假设 prefix_sum 是数组 $A$ 的前缀和,即 prefix_sum[i] 表示 $A$ 中前 $i$ 个元素的和,则区间 $[i,j]$ 的和为:

$$\sum_{k=i}^j A[k] = \sum_{k=1}^j A[k] - \sum_{k=1}^{i-1} A[k] = prefix_sum[j]-prefix_sum[i-1]$$

有了前缀和,我们只需要计算长度为 $k$ 的子区间的和,即

for k in range(1, n+1):
    for i in range(n-k+1):
        j = i + k - 1
        sub_sum = prefix_sum[j] - prefix_sum[i-1]
        sums.append(sub_sum)

最终,我们只需要在所有子区间的和中,选取和在区间 $[l, r]$ 中的子区间的和,并对结果取模,即可得到答案。具体实现参见代码片段。

代码实现
def subarray_sum(n, A, l, r):
    MOD = 1000000007
    prefix_sum = [0] * (n+1)
    for i in range(1,n+1):
        prefix_sum[i] = prefix_sum[i-1] + A[i-1]
    sums = []
    for k in range(1, n+1):
        for i in range(n-k+1):
            j = i + k - 1
            sub_sum = prefix_sum[j] - prefix_sum[i-1]
            sums.append(sub_sum)
    ans = 0
    for sub_sum in sums:
        ans += (l <= sub_sum <= r)
    return ans % MOD

返回的是markdown格式的文本。

## 题目14

本题涉及对于整数数组的计算。

### 问题描述

给出一个长度为 $n$ 的整数数组 $A$, 以及两个整数 $l$ 和 $r$。 编写一个程序以确定 $A$ 中所有子数组 $A[i:j]$ 的和之和,并返回结果对 $10^9+7$ 取余数的结果。 

举个例子,如果 $A=[1, 2, 3]$, $l=1$, $r=2$, 则可能的子区域为 $[1], [2], [3], [1, 2], [2, 3]$ and $[1, 2, 3]$, 其中 $[1, 2]$ 和 $[2, 3]$ 的和均在区间 $[l, r]$ 中。 

**输入格式:**

- 第一行输入整数 $n$,表示整数数组的长度 $(1\leq n\leq 10^5)$;
- 第二行输入 $n$ 个整数,分别表示 $A[1],A[2],\cdots,A[n]$ $(0\leq A[i]\leq 10^9)$ ;
- 第三行输入两个整数 $l$ 和 $r$ $(1\leq l\leq r\leq n)$。

### 示例

输入

3 1 2 3 1 2


输出

6


解释

可能的子区域为 $[1], [2], [3], [1,2], [2,3]$ 和 $[1,2,3]$,其中 $[1,2]$ 和 $[2,3]$ 的和均在区间 $[1,2]$ 中,总和为 $3 + 5 = 8$,对应的答案为 $8\%1000000007=8$。

### 解题思路

这道题目实际上要求的是数组 $A$ 的所有子区间中和在 $[l,r]$ 之间的子区间和的和。对于这道题目,我们可以计算出所有子区间的和,再从中选取和在 $[l,r]$ 之间的子区间和,最后对结果取模。

因此,我们需要计算出所有子区间的和。具体来说,对于 $[i,j]$ 这个区间,其和为 $\sum_{k=i}^j A[k]$。

我们可以使用两重循环来计算出所有的子区间和:

``` python
sums = []
for i in range(n):
    for j in range(i,n):
        sub_array = A[i:j+1]
        sub_sum = sum(sub_array)
        sums.append(sub_sum)

其中,sums 列表用于记录所有的子区间和。由于子区间可能非常多,因此时间复杂度为 $O(n^3)$,无法通过本题。

现在的关键在于如何优化这个算法。注意到每一个子区间是由相邻的子区间转移而来的。例如,对于一个数组 $A=[1,2,3,4]$,其子区间包括:

[1]
[2]
[3]
[4]
[1,2]
[2,3]
[3,4]
[1,2,3]
[2,3,4]
[1,2,3,4]

我们可以观察到,左侧的其中前4个子区间,是由左侧第一个子区间与右侧第一个元素组合而来的。后5个子区间中,左侧的其中前3个子区间,是由第5个子区间和右侧第二个元素组合而来的。再往右推,就可以看出所有子区间都是由相邻的子区间转移而来的。

因此,这个算法的优化方向就很明显了:计算出所有长度为 $k$ 的子区间的和,再使用这个和去计算长度为 $k+1$ 的子区间的和。直到计算出所有长度为 $n$ 的子区间的和为止。

具体来说,我们可以通过计算前缀和 $\sum_{i=1}^j A[i]$ 去计算出任意子区间 $[i,j]$ 的和。假设 prefix_sum 是数组 $A$ 的前缀和,即 prefix_sum[i] 表示 $A$ 中前 $i$ 个元素的和,则区间 $[i,j]$ 的和为:

$$\sum_{k=i}^j A[k] = \sum_{k=1}^j A[k] - \sum_{k=1}^{i-1} A[k] = prefix_sum[j]-prefix_sum[i-1]$$

有了前缀和,我们只需要计算长度为 $k$ 的子区间的和,即

for k in range(1, n+1):
    for i in range(n-k+1):
        j = i + k - 1
        sub_sum = prefix_sum[j] - prefix_sum[i-1]
        sums.append(sub_sum)

最终,我们只需要在所有子区间的和中,选取和在区间 $[l, r]$ 中的子区间的和,并对结果取模,即可得到答案。具体实现参见代码片段。

代码实现
def subarray_sum(n, A, l, r):
    MOD = 1000000007
    prefix_sum = [0] * (n+1)
    for i in range(1,n+1):
        prefix_sum[i] = prefix_sum[i-1] + A[i-1]
    sums = []
    for k in range(1, n+1):
        for i in range(n-k+1):
            j = i + k - 1
            sub_sum = prefix_sum[j] - prefix_sum[i-1]
            sums.append(sub_sum)
    ans = 0
    for sub_sum in sums:
        ans += (l <= sub_sum <= r)
    return ans % MOD