试题 历届试题
问题描述
观察这个数列: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;
}