波动数列

问题描述
  观察这个数列:
  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。
 
  思路:数论+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\n",dp[n][s%n]);
27     //若sum=s(mod n)则可以改变首项使得数列满足条件
28     return 0;
29 }

 

 

 

posted @ 2015-03-07 14:45  daydaycode  阅读(294)  评论(0编辑  收藏  举报