蓝桥杯 波动数列

问题描述
  观察这个数列:
  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。


当n等于1的时候,很明显方案数为1
当n>1时, 假设波动序列为  x,x+d1,x+d1+d2, ... , x+d1+d2+...+dn-1
构造差值序列:             {d}={d1,d2,d3,...,dn-1}
把{d}序列数列翻一下,得到f序列: {f}={dn-1,...,d3,d2,d1}
则 s =x+(x+d1)+(x+d1+d2)+...+(x+d1+d2+...+dn-1)
   =nx+(n-1)d1+(n-2)d2+...+dn-1
   =nx+dn-1+2dn-2+....+(n-1)d1
   =nx+f1+2f2+...+(n-1)fn

由于第一个数x是整数,所以原问题等价于求
满足式子   s=f1+2f2+...+(n-1)fn-1   (mod n)
的{f}序列的方案数,其中{f}序列的元素只能是a或者-b

从上面红色的式子,我们可以看出,当s增加n的整数倍时
     满足  s+tn=f1+2f2+...+(n-1)fn-1 (mod n)
就等同于满足    s=f1+2f2+...+(n-1)fn-1 (mod n)
所以当s增加n的整数倍的时候,方案数是不会变的

比如在n=4时, s=7和s=3求解出来的方案数是一样的
所以我们没必要对于每个s都求解出结果,只需要求解出 s=0 s=1 s=2 s=3 就可以
s=7的解就同于s=3的解 g(7)=3
s=8的解等同于s=0的解 g(8)=0
。。。
规定g函数为: 整数->{0,1,2,...,n-1}的映射


假设 f[g(s),n-1]为满足式子
  s=f1+2f2+...+(n-1)fn-1   (mod n)
的方案数
 那么有递推公式:        sa=s-(n-1)*a;
                   sb=s+(n-1)*b
                   f[g(s),n-1]=f[g(sa ),n-2]+f[g(sb) ,n-2]
当fn-1取a时, sa=s-(n-1)*a
当fn-1取-b时,sb=s+(n-1)*b
再把sa,sb代入 f[g(s),(n-1)-1]就可以得到上面的递推关系式


现在,我们来手算题目给的例子  4 10 2 3(n,s,a,b)


一开始初始化第一列为1,0,0,0
接着一列一列开始填数( f[i,j]=f[g(i-j*a),i-1]+f[g(i+j*b),i-1])
f[0,1]=f[g(-2),0]+f[g(3),0]
    =f[2,0]+f[3,0]
    =0+0
    =0
把一列填完,接着填第二列
f[0,2]=f[g(-4),1]+f[g(6),1]
    =f[0,1]+f[2,1]
    =0+1
    =1
这样一列列填完。。。
填完这个f[n][n]的数组之后

要求 s=10,n=4的方案数 即是 f[g(10)][3]
返回f[0][3]的值就是答案2

接下来只需要把手算的过程用机器算就行
对于输入的n
如果n=1,返回1
如果n>1,机器模拟填数过程,返回f[g(s)][n-1]

代码如下:
import java.util.Scanner;
public class Main {  
    static int n, s, a, b;  
    static int f[][];  
    static int modnum = 100000007; 
    
    public static void main(String[] args) {
    	Scanner sc=new Scanner(System.in);
    	
    	n=sc.nextInt();
    	s=sc.nextInt();
    	a=sc.nextInt();
    	b=sc.nextInt();
    	f=new int[n][n];
    	f[0][0]=1;
		System.out.println(c());
	}
    
    
    private static int c()
    {
    	if(n==1)
    		return 1;
    	
    	for(int j=1;j<n;j++)
    		for(int i=0;i<n;i++)
    			f[i][j]=(f[g(i-j*a)][j-1]+f[g(i+j*b)][j-1])% modnum; 
    			
    	return f[g(s)][n-1];
    }
    
    private static int g(int v)  
    {
    	return ((v%n)+n)%n;
    }
}



这道题比较难理解,本人讲解水平有限,看不懂的直接看代码把







   
 
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值