蓝桥学习 PREV-30

试题 历届试题

PREV-30 波动数列

问题描述

观察这个数列:1 3 0 2 -1 1 -2 …这个数列中后一项总是比前一项增加2或者减少3。栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?

输入格式

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

输出格式

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

样例输入

4 10 2 3

样例输出

2

思路:

有一说一这一题看起来不难,但是其实做起来特别的麻烦,而且需要判断的东西很多,而且测试的数据很大,无法进行简单的暴力搜索,所以得换一种思路,首先我们需要判断这个固定长度且固定和的变换次数,按照等差数列的求和公式,总的操作和为n*(n-1)/2,因为每操作一次就要从上一次的操作加一,总和就是如此,那我们就要假设所有的可能性, 设dp(i,j)表示从集合U(假设集合中元素从小到大排列)中前 i 个数中选择,使得和为 j 总共有几种选择 。
设数列首项为t,F(i)={a,-b},第i项加上F(i)为第i+1项。所以第2项可表示为t+F(1),第2项可表示为t+F(1)+F(2),第3项可表示为t+F(1)+F(2)+F(3)…把第一项到第n项累加起来可得表达式n_t + (n-1)F(1) + (n-2)F(2) + … +F(n-1) =s。n_t = s - {(n-1)F(1) + (n-2)F(2) + … +F(n-1)}。由于F(i)不为a就为-b,设有c个a,F(i)的系数相加共有1+2+…+n-1 = (n-1)_n/2项目,所以共有c-(n-1)_n/2个b。所以令temp = s - c_a + ((n-1)_n/2 - c)_b,当temp%n = = 0时,说明temp == n_t,满足要求。并且,c为1n-1个数中的若干个数组合相加而得的,每一种组合都是一种方案,到此,我们就可以发现这题已经转化为求01背包问题的方案数了。即从体积为1n-1的物品中有几种方案能够恰好填满背包。然后再将dp用滚动数组来进行空间压缩。

代码:

#include<iostream>
using namespace std;
typedef long long ll;
const ll mod=100000007;
ll mu[10000006];
int main(){
	ll s,num=0;
	ll n,a,b,m;mu[0]=1;
	cin>>n>>s>>a>>b;
	for(ll i=1;i<n;i++){
		for(ll j=i*(i+1)/2;j>=i;j--){
			mu[j]=(mu[j-i]+mu[j])%mod;
		}
	}
	for(ll i=0;i<=n*(n-1)/2;i++){
		ll ans=s+a*i-(n*(n-1)/2-i)*b;
		if(ans%n==0)num=(num+mu[i])%mod;
	}
	cout<<num<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值