题目描述
观察这个数列:1 3 0 2 -1 1 -2 …这个数列中后一项总是比前一项增加2或者减少3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?
输入
输入的第一行包含四个整数 n s a b,含义如前面说述。
1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。
输出
输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以100000007的余数。
样例输入
4 10 2 3
样例输出
2
解题思路:
我们不妨设ddd为+a+a+a或者为−b-b−b,则d=(+a,−b)d = (+a,-b)d=(+a,−b),然后我们就知道这个数列是这样的:
x,x+d1,x+d1+d2,⋯,x+d1+d2+⋯+dn−1x ,x+d_1,x+d_1+d_2,\cdots,x+d_1+d_2+\cdots+d_{n-1}x,x+d1,x+d1+d2,⋯,x+d1+d2+⋯+dn−1
这个数列的合为sss,所以我们可以得到:
nx+(n−1)d1+(n−2)d2+⋯+dn−1=snx+(n-1)d_1+(n-2)d_2+\cdots+d_{n-1} = snx+(n−1)d1+(n−2)d2+⋯+dn−1=s
然后得到:
s−[(n−1)d1+(n−2)d2+⋯+dn−1]n\frac{s-[(n-1)d1+(n-2)d2+\cdots+d_{n-1}]}{n}ns−[(n−1)d1+(n−2)d2+⋯+dn−1]===xxx
所以我们可以知道s模n与[(n−1)d1+(n−2)d2+⋯+dn−1]模n相等s模n与[(n-1)d_1+(n-2)d_2+\cdots+d_{n-1}]模n相等s模n与[(n−1)d1+(n−2)d2+⋯+dn−1]模n相等
又因为dnd_ndn = (+a,−b)(n=1,2,3,...,n)(+a,-b) (n = 1,2,3,...,n)(+a,−b)(n=1,2,3,...,n),所以我们可以得到:
s模n与[d1+2d2+⋯+(n−1)dn−1]模n相等s模n与[d_1+2d_2+\cdots+(n-1)d_{n-1}]模n相等s模n与[d1+2d2+⋯+(n−1)dn−1]模n相等
现在我们设dp[i][j]表示表示要选i个a或者-b且余数为j的所有集合的数量。
那么我们现在思考关系表达式:
现在我们要选的是第i项的d,意思就是第i项的d是要+a,还是-b,在第i项前面的,都是已经选好的了,所以:
我们设第i项前面的d加起来总和为C,然后我们可以根据
d1+2d2+⋯+(n−1)dn−1d_1+2d_2+\cdots+(n-1)d_{n-1}d1+2d2+⋯+(n−1)dn−1,可以得到:
(C+i∗di)模n=j(C+i*d_i)模n =j(C+i∗di)模n=j
那么C模n就等于(j−i∗di)模nj - i*d_i)模nj−i∗di)模n
则得到关系表达式:
f[i][j] = (f[i-1][get_mod(j-a*i,n)]+f[i-1][get_mod(j+b*i,n)])%MOD;
这里我们之所以对a模b要用(a%b+b)%b的形式,是因为C++中的%与数学上的取模不太一样,举个例子:
1.C++:-2%3 = -2,出现了负数,在数组中a[i],i不能为负,因此要转换。
2.数学上:-2%3 = 1
所以要用这个公式让C++进行数学上的取模(a%b+b)%b,只要C++取模以后得到的结果可能为负数,推荐都用公式进行这样的转换:
C++手写a除以b的正余数
然后想想如何初始化,初始化也很简单,dp[0][0] = 1,他选0项,那么总和肯定是0,0模n也是0,所以为1
代码如下:
#include <iostream>
using namespace std;
const int N = 1010;
int dp[N][N];
const int MOD = 100000007;
int get_mod(int a,int b)
{return (a%b+b)%b;
}int main()
{int n,s,a,b;cin>>n>>s>>a>>b;dp[0][0] = 1;for (int i = 1;i<n;i++)for (int j = 0;j<n;j++)dp[i][j] = (dp[i-1][get_mod(j-a*i,n)]+dp[i-1][get_mod(j+b*i,n)])%MOD;cout<<dp[n-1][get_mod(s,n)]<<endl;return 0;
}