波动数列

[蓝桥杯 2014 省 A] 波动数列

题目描述

观察这个数列:

$1,3,0,2,-1,1,-2, \cdots $。

这个数列中后一项总是比前一项增加 \(2\) 或者减少 \(3\)

栋栋对这种数列很好奇,他想知道长度为 \(n\) 和为 \(s\) 而且后一项总是比前一项增加 \(a\) 或者减少 \(b\) 的整数数列可能有多少种呢?

输入格式

输入的第一行包含四个整数 \(n,s,a,b\),含义如前面说述。

输出格式

输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以 \(100000007\) 的余数。

样例 #1

样例输入 #1

4 10 2 3

样例输出 #1

2

提示

【样例说明】

这两个数列分别是 2 4 1 3 和 7 4 1 -2。

【数据规模与约定】

对于 \(10\%\) 的数据,\(1 \le n \le 5\)\(0 \le s \le 5\)\(1 \le a,b \le 5\)

对于 \(30\%\) 的数据,\(1 \le n \le 30\)\(0 \le s \le 30\)\(1 \le a,b \le 30\)

对于 \(50\%\) 的数据,\(1 \le n \le 50\)\(0 \le s \le 50\)\(1 \le a,b \le 50\)

对于 \(70\%\) 的数据,\(1 \le n \le 100\)\(0 \le s \le 500\)\(1 \le a,b \le 50\)

对于 \(100\%\) 的数据,\(1 \le n \le 1000\)\(-10^9 \le s \le 10^9\)\(1 \le a,b \le 10^6\)

题解

不妨设数列首项为\(a_1\),操作数为\(x\),其中\(x = a\)  \(or\)  \(x = -b\),易知\(s=\sum_1 ^n a_1+(i-1)x\), 即$$s=na_1+\frac{n(n-1)}{2}x$$由于\(n, a, b, s\)都是整数,且波动数列为整数数列,则 \(a_1 \in Z\),记 \(z = \frac{n(n-1)}{2}x\),则 \(a_1=\frac{s-z}{n}\),即 \(s\) mod \(n\) = \(z\) mod \(n\). 题目中已给定\(s、n\)的值,那么题意转化为有多少种方案能使得 \(z\)\(s\)\(n\) 同余,并满足数列长度为 \(n\) 项且和为 \(s\). 设 \(dp_{i,j}\) 表示 \(z\) 的前 \(i\) 项的序列和模 \(n\) 后值为 \(j\) 的方案数,其中 \(i \in [0, n-1]\) .

  1. \(z\) 的前 \(i\) 项序列和为 \(z_i\) , 即 \(z_i=x * (0+1 + 2 + ... + i)\) (注:事实上 \(0+1 + ... + i\) 不能写成求和的形式,因为 \(x\) 并不是一个确定的自然数,对于某一项我们取\(a\)还是取\(b\)是不一定的,上文推导中记作求和仅仅是为了方便)
  2. \(z\) 的前 \(i\) 项序列和 \(z_i\)\(n\)\(j\),即 \(j=0+x+2x+...+i·x\) mod \(n\)

因为\(x = a\)  \(or\)  \(x = -b\),那么第 \(i\) 项将由第 \(i-1\) 项推出,即状态转移方程为:$$dp_{i,j}=dp_{i-1,Mod(j-i·a)} + dp_{i-1,Mod(j+i·b)}$$

  1. \(Mod (x) =\) ( \(x\) mod \(n + n\) ) mod \(n\). 这样能够保证余数是个正数.
  2. \((a \pm b)\) mod \(n\) = ( \(a\) mod \(n\) \(\pm\) \(b\) mod \(n\) ) mod \(n\)
  3. \(z_i = z_{i-1} + i · a\) or \(z_i = z_{i-1} - i · b\),考察该式:\(z_{i-1}=z_i - i·a\), 因为 \(Mod(z_i)= j\),两边取余可得: $$Mod(z_{i-1})=Mod(z_i - i·a)$$ $$Mod(z_i-i·a)= Mod(j - i·a)$$
  4. 初始值\(dp_{0,0}=1\)

代码:

#include<cstdio>
typedef long long LL;
const LL N = 1e3 + 3, mod = 1e8 + 7;
LL n, dp[N][N]={0};

int Mod(LL x)
{
	return ( x % n + n ) % n;
}

int main(){
	LL s,a,b; 
	scanf("%lld%lld%lld%lld",&n,&s,&a,&b);
	dp[0][0] = 1;
	for(LL i = 1; i < n; i++)
		//由于j是对n取模后的结果,则j的范围是[0, n-1]
		for(LL j = 0; j < n; j++)
			dp[i][j] = (dp[i-1][Mod(j-a*i)]+dp[i-1][Mod(j+b*i)])%mod;
	printf("%lld", dp[n-1][Mod(s)]);
	return 0;
}
posted on 2023-03-20 15:22  sc01  阅读(25)  评论(0编辑  收藏  举报