问题描述
观察这个数列:
1 3 0 2 -1 1 -2 ...
这个数列中后一项总是比前一项增加2或者减少3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?
1 3 0 2 -1 1 -2 ...
这个数列中后一项总是比前一项增加2或者减少3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?
输入格式
输入的第一行包含四个整数 n s a b,含义如前面说述。
输出格式
输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以100000007的余数。
样例输入
4 10 2 3
样例输出
2
样例说明
这两个数列分别是2 4 1 3和7 4 1 -2。
数据规模和约定
对于10%的数据,1<=n<=5,0<=s<=5,1<=a,b<=5;
对于30%的数据,1<=n<=30,0<=s<=30,1<=a,b<=30;
对于50%的数据,1<=n<=50,0<=s<=50,1<=a,b<=50;
对于70%的数据,1<=n<=100,0<=s<=500,1<=a, b<=50;
对于100%的数据,1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。
对于30%的数据,1<=n<=30,0<=s<=30,1<=a,b<=30;
对于50%的数据,1<=n<=50,0<=s<=50,1<=a,b<=50;
对于70%的数据,1<=n<=100,0<=s<=500,1<=a, b<=50;
对于100%的数据,1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。
思路:数论+DP. 总和s如此巨大,不能开个如此大的DP数组。设N个数的和为sum, 如果sum%n==s%n==r, 那么sum=k*n+r. 也就是对n个数每个数+k1, sum+k1*n=s; 问题转换为和的余数为r=s%n的组合有多少个。 那么数组就可以开得下了。
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 const int Mod=100000007; 9 int dp[1005][1005]; 10 int main() 11 { 12 int n,s,a,b; 13 scanf("%d%d%d%d",&n,&s,&a,&b); 14 dp[1][0]=1;//不妨设首项为0 15 for(int i=1;i<n;i++) 16 { 17 for(int j=0;j<n;j++) 18 { 19 //计算每一次+a或者-b对数列之和sum的贡献 20 dp[i+1][(j+(n-i)*a)%n]=(dp[i+1][(j+(n-i)*a)%n]+dp[i][j])%Mod; 21 dp[i+1][(j+i*b)%n]=(dp[i+1][(j+i*b)%n]+dp[i][j])%Mod; 22 //等价于dp[i+1][(j-(n-i)*b)%n]+=dp[i][j]; 23 } 24 } 25 s=(s+1000000000LL*n)%n;//保证s非负且不改变s mod n的值 26 printf("%d ",dp[n][s%n]); 27 //若sum=s(mod n)则可以改变首项使得数列满足条件 28 return 0; 29 }