小M的幸运数列变换(线性解法)|豆包MarsCode AI刷题

51 阅读4分钟

问题理解

题目链接

小M拥有一个长度为n的数组a,他希望通过一系列操作将数组中的所有元素都变为目标值w。每次操作可以选择一个区间 [l, r],并将该区间内的所有元素都加1。但有两个限制条件:

  1. 操作的起点l必须唯一:每次操作选择的起点l不能与之前的任何操作相同。
  2. 操作的终点r必须唯一:每次操作选择的终点r不能与之前的任何操作相同。

我们的目标是计算有多少种不同的操作方案可以实现将数组a全部转化为w。注意,操作的顺序不同但操作的区间相同,视为同一种操作方案。

解题思路

要解决这个问题,我们可以按照以下步骤进行:

1. 计算每个元素需要增加的次数

首先,对于数组中的每个元素a[i],计算它需要增加多少次才能达到目标值wci=wa[i]c_i = w - a[i]

  • 注意:如果任何一个c_i为负数,意味着该位置的元素已经大于w,无法通过加操作使其等于w,此时答案为0

2. 计算差分数组

接下来,计算每个位置的差分值d[i],表示当前位置需要的操作次数与前一个位置需要的操作次数之差: di=cici1d_i = c_i - c_{i-1} 其中,定义c0=0c_0 = 0

  • 条件验证: - 每个d_i必须满足-1 ≤ d_i ≤ 1。如果有任何d_i不满足这个条件,说明无法通过满足限制条件的操作方案实现目标,答案为0

3: 统计特定条件的位置数 k

在这个步骤中,我们需要从差分数组中找到一些特殊的位置,并统计这些位置的数量 k。这些位置符合两个特定的条件:

  • 条件 1:di=0d_i = 0
  • 条件 2:ci>0c_i > 0

让我们逐个解析这两个条件,并解释为什么它们对最终操作方案数的计算非常重要。

条件 1: di=0d_i = 0

回顾一下差分数组 d_i 的定义: di=cici1d_i = c_i - c_{i-1}

其中,c_i 是数组 a 中元素到目标值 w 的差,计算方式是 ci=wa[i]c_i = w - a[i]

  • d_i = 0 表示当前位置 i 需要的增加次数和前一个位置 i-1 需要的增加次数相同。换句话说,在位置 i,我们并不需要改变前一个位置已经增加的操作量。换个角度看,如果 d_i = 0,意味着在当前位置,我们可以选择是否增加一个新的操作,而不影响前面已经进行的操作

条件 2: ci>0c_i > 0

  • c_i > 0 意味着在位置 i,该位置的元素 a[i] 需要增加 c_i 次才能变成目标值 w
  • 这个条件确保我们不是在一个已经符合目标的地方(即 c_i = 0),而是在一个确实需要增加操作的地方(即 c_i > 0)。

结合两个条件

当这两个条件同时满足时,意味着在这个位置 i,我们可以有两种选择:

  1. 不进行任何操作:我们保持前一个操作的影响。
  2. 开始并结束一个新的操作:我们选择在这个位置 i 开始并结束一个新的区间操作,即使得a[i]的值增加。

举个例子,假设我们当前有一个区间 [l, r],操作范围覆盖从 lr 的所有元素。现在,如果 d_i = 0c_i > 0,我们可以选择是否在位置 i 开始一个新的区间操作,使得 a[i] 变成 w,而不影响前面的操作。

为什么会影响操作方案数?

假设我们有一个位置 i 满足 d_i = 0c_i > 0,那么在这个位置,我们可以选择进行操作或不进行操作。这就意味着我们有两种选择来决定是否在当前位置 i 添加一个新的操作。

所以,每一个满足这两个条件的位置都会提供两种选择:我们可以选择增加操作,也可以不增加操作。因此,满足条件的位置数 k 会影响最终操作方案的数量。

故此最终的方案数是 2^k,其中 k 是满足这两个条件的位置数。

代码实现 时间复杂度O(n)O(n)

#include <bits/stdc++.h>
using namespace std;
// Edit your code here
inline int qmi(int a, int b) {
  int ans = 1;
  for (; b; b >>= 1) {
    if (b & 1)
      ans = ans * a;
    a = a * a;
  }
  return ans;
}
int solution(int n, int w, const std::vector<int> &a) {
  vector<int> d(n + 1);
  for (int i = 0; i < n; i++) {
    d[i] = w - a[i];
    if (d[i] < 0)
      return 0;
  }
  for (int i = 1; i < n; i++) {
    d[i] -= d[i - 1];
    if (abs(d[i]) > 1)
      return 0;
  }
  if (abs(d[0]) > 1)
    return 0;
  int k = 0;
  for (int i = 0; i < n; i++) {
    if (!d[i] && (w - a[i]) > 0)
      k++;
  }
  return qmi(2, k);
}
int main() {
  // Add your test cases here

  std::cout << (solution(2, 2, std::vector<int>{1, 1}) == 2) << std::endl;
  std::cout << (solution(3, 3, std::vector<int>{1, 2, 3}) == 0) << std::endl;

  return 0;
}