藍橋杯-波動數列

小程xy發表於2024-05-11

觀察這個數列:

1 3 0 2 -1 1 -2 …

這個數列中後一項總是比前一項增加2或者減少3,且每一項都為整數。

棟棟對這種數列很好奇,他想知道長度為 n 和為 s 而且後一項總是比前一項增加 a 或者減少 b 的整數數列可能有多少種呢?

輸入格式

共一行,包含四個整數 n,s,a,b,含義如前面所述。

輸出格式

共一行,包含一個整數,表示滿足條件的方案數。

由於這個數很大,請輸出方案數除以 100000007 的餘數。

資料範圍

1≤n≤1000,
−1e9≤s≤1e9,
1≤a,b≤1e6

輸入樣例:

4 10 2 3

輸出樣例:

2

樣例解釋

兩個滿足條件的數列分別是2 4 1 3和7 4 1 -2。

題解:

化簡等式:

設第一個數是x, d等於a或b)
x, x + d1, x + d1 + d2 ... (x +...+ d(n - 1)) == s
=> (n) * x + (n - 1) * d1 + (n - 2) * d2 ... 1 * d(n - 1) = s
=> x = (s - (n - 1) * d1 - (n - 2) * d2 ... 1 * d(n - 1)) / n

由於 x 只要是整數就行, 所以只要 (s - (n - 1)d1 - (n - 2)d2 ... 1xd(n - 1))是 n 的倍數就行, 也就是 s % n == ( (n - 1)d1 + (n - 2)d2 ... 1xd(n - 1) ) % n

這裡有個結論: 兩個整數數 a, b. 如果 a % k == b % k, 那麼說明 abs(a - b)一定是n的倍數。 藍橋杯也出一個相關的題目 -> k倍區間

dp分析

常見疑惑: 為什麼是狀態計算是f[i][j] = f[i - 1][j - (n - i) * a] + f[i - 1][j + (n - i)b]

  • 看到這裡, 你要理解,等式左邊f[i][j]中的 j是第i個選擇的是a或b的序列的和對 n 取模後的餘數, 這個序列裡面是不包含初始值x的, 這也是為什麼 i是[1,n),只迴圈了n - 1次, (一共n個數, 我們只需要選n - 1次)
  • 觀察等式 (n - 1) * d1 + (n - 2) * d2 ... 1 * d(n - 1) % n = j 可以發現 從左往右 第 i 項(不含第i項)的係數是 (n - i), 那麼前 i 項的和應該是 j - (n - i)a 或 j + (n - i)b (d是a或-b)

要理解轉移這個思想, 從 i - 1, j - (n - i) * a 、 j + (n - i)b 轉移到f[i][j]
ac程式碼👇

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10, MOD = 100000007;
int f[N][N];
int get_mod(int a, int b)  //由於a%b可能是負數, 我們要的是正數, 這個函式可以實現
{
    return (a % b + b) % b;
}

int main()
{
    int n, s, a, b; cin >> n >> s >> a >> b;
    
    f[0][0] = 1;
    
    for (int i = 1; i < n; i ++)  // 只選擇 i - 1次數 (長度為n的序列, 只有 n - 1個間隙)
        for (int j = 0; j < n; j ++)  // j - (n - i)a是前i項的和, get_mod是讓其與 n 相除取餘數
            f[i][j] = f[i - 1][get_mod(j - (n - i) * a, n)] % MOD + f[i - 1][get_mod(j + (n - i) * b, n)] % MOD;

    cout << f[n - 1][get_mod(s, n)] % MOD << endl;
    return 0;
}