波动数列
波动数列
Description
观察这个数列:
1 3 0 2 -1 1 -2 ...
这个数列中后一项总是比前一项增加2或者减少3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?
Input
输入的第一行包含四个整数 n s a b,含义如前面说述。
Output
输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以100000007的余数。
粘一下大佬的代码!滚动数组实在看不懂,但是这个非常可以!
/* 思路:设第一个元素为a0:若想让最后和为s 则要满足 sum = a[0]+a[1]+...+a[n-1] = s 由于a[k] 是由 a[k-1] 通过+a或者-b得到的 所以任何 i < k 若a[i]都会对他产生影响 例如:若每次都是加a操作则a[]的值如下 a0 a0 a1 a0 + a a2 a0 + a + a a3 a0 + a + a + a ..... 也就是说第一次加a 那么 sum 就会加上n-1个a 然后依次递减一个,减b相似 但是不管加a或者减b,总的操作的次数为max = (n-1)*n/2次; 其中+a的次数可能为 0到max任何值。一旦+a的次数确定了不妨设为numa, -b的次数随之确定,又因为 s = N*a0 + numa*a - (max-numa)*b; 若枚举+a的次数,其中只有a0未知,如果a0有整数解,则满足,就加上 该种情况下的方案数。 最后就是计算方案数,一旦numa给定,总的可能的方案有多少呢? 其实问题一转化,就变成了求从 1,2... n-1 任意取数,能够组成numa的 方案数,设该问题为dp[n-1][numa]; 对于一般的dp[i][j] 表示从1-i中选出 任意个数,使得和为j对第i个数特殊考虑,就有第i个数存在于j内有dp[i-1][j-i]种 方案。若不在则有dp[i-1][j]种方案。 所以 : dp[i][j] = dp[i-1][j] + dp[i-1][j-i]; 若用滚动数组,就可省去一维。 */ #include <iostream> using namespace std; typedef long long ll; const ll mod = 100000007; ll dp[10000006]; int main() { ll n,s,a,b; cin>>n>>s>>a>>b; ll ans = 0; dp[0] = 1; for(ll i = 1; i < n; i++) for(ll j = i*(i+1)/2; j >= i; j--) dp[j] = (dp[j-i] + dp[j])%mod; for(ll i = 0; i <= (n-1)*n/2; i++) { ll tmp = s + i*a - ((n-1)*n/2 - i) * b; if(tmp % n == 0) ans = (ans+dp[i])%mod; } cout << ans <<endl; return 0; }
浙公网安备 33010602011771号