我们将最后剩下的数写成\(k\)进制数,即\(0.a_1a_2a_3a_4…\)
\(\therefore a_i\)代表最后第\(i\)次合并了\(a_i\)与\(k-a_i\)个\(0\)所得到的数.
所以我们可以设\(dp[i][j]\)表示当前的数已经有了\(i\)位,\(\sum_{j=1}^{i}a_i=j\)的方案数.
显而易见的,\(dp[i][j]=\sum_{t=\max(1,j-k)}^{j}dp[i-1][t]\).
如果状态合法,即\(j\equiv m(mod\ k)\)并且\(-j\equiv n(mod\ k)\)并且\(i*k-j\leq n\)
\[ \begin{align} ans+=\sum_{t=\max(1,j-k)}^{j-1}dp[i-1][t] \end{align} \]
为什么\(ans\)不需要加上\(dp[i-1][j]\)呢?
因为这个\(k\)进制数的末尾不能是\(0\).
如果先累加前缀和再累假答案,就让末尾为\(0\)的情况变成了合法情况,这是不对的.
最后记得取模.
#pragma GCC optimize(3)
#include
#define il inline
#define rg register
#define gi read
using namespace std;
const int O = 4e3 + 10, mod = 1e9 + 7;
template
il TT read() {
TT o = 0,fl = 1; char ch = getchar();
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') fl = -1, ch = getchar();
while (isdigit(ch)) o = o * 10 + ch - '0', ch = getchar();
return fl * o;
}
bool now;
int n, m, k, res, ans, dp[2][O];
il void Add(int & x, int y) {x += y; if (x >= mod) x -= mod;}
il void Minus(int & x, int y) {x -= y; if (x < 0) x += mod;}
int main() {
n = gi() - 1, m = gi(), k = gi() - 1;
for (int i = 1; i <= n + m; ++i) {
res = 1;
for (int j = 1; j <= m; ++j) {
if (j > k) Minus(res, dp[!now][j - k - 1]);
if (j % k == m % k && (k * i - j) % k == n % k && k * i - j <= n)
Add(ans, res);
Add(res, dp[!now][j]);
dp[now][j] = res;
j == k ? --res : 0;
}
now ^= 1;
}
printf("%d\n", ans);
return 0;
}