1. 程式人生 > 實用技巧 >試題 歷屆試題 波動數列

試題 歷屆試題 波動數列

連結:https://www.acwing.com/problem/content/description/1216/

原題解:https://www.acwing.com/solution/content/7507/

用到了數論和動態規劃的知識。假定第一個數為x,第二個數為x + p1(pi為+a或者-b),第三個數為x + p1 + p2, ......,由於和為s,則n * x + (n - 1) * p1 + (n - 2) * p2 + ...... + p(n-1) == s,

則此時(重點來了):

          s≡(n - 1) * p1 + (n - 2) * p2 + ...... + p(n-1)

(mod n)。

對於mod n,最多1000種可能,我們設立d陣列d[i][j]的i代表(n - 1) * p1+ (n - 2) * p2+ ...... + p(n-1)數列從後往前選取了i個p的時候餘數為j的情況下擁有的可能數量。

由此我們得到遞推公式:

          d[i][j] = d[i - 1][j - a * i] + d[i - 1][j + b * i];

(pi的係數,(最後一個p(n-1)的係數是1,第一個的係數是(n-1),每次決定p是+a還是-b時都要乘以係數。)

由於s與數列對n同模,則d[n -1][s % n]代表選取了從後往前的n-1項(也就是全選完時),同時數列餘數與s關於n同模的結果。

此時的個數就是最終要的答案,因為數列的值加上x個n最後的和就是s,所以全選完時,數列的和的餘數為s%n的情況下加上(x+y)個n(因為數列的和也對n求過模了,我們假設和為d[n-1][s%n] + y * n)就是s了,而其他餘數均不可能滿足題目要求。

還要注意,算出來的j-a * i或是j + b * i 或是給的s都有可能為負數,得到一個正數的下標才能得到正確的答案,所以我們採用return (x % n + n) % n得到x(不管正負)關於n的模。

ac程式碼:

#include <bits/stdc++.h>
using namespace std;
int d[1005
][1005]; const int MOD = 100000007; int n, s, a, b; int cal(int x) { return ((x % n) + n) % n;//(x + n)不一定大於n,所以不能用(x + n) % n來得到餘數 } int main() { scanf("%d %d %d %d", &n, &s, &a, &b); d[0][0] = 1; for (int i = 1; i < n; ++i) { for (int j = 0; j < n; ++j) { d[i][j] = (d[i - 1][cal(j - a * i)] + d[i - 1][cal(j + b * i)]) % MOD; } } printf("%d\n", d[n - 1][cal(s)]);//s可能為負數 return 0; }