观察这个数列:
1 , 3 , 0 , 2 , − 1 , 1 , − 2 , ⋯ 1,3,0,2,-1,1,-2, \cdots 1,3,0,2,−1,1,−2,⋯。
这个数列中后一项总是比前一项增加 2 2 2 或者减少 3 3 3。
栋栋对这种数列很好奇,他想知道长度为 n n n 和为 s s s 而且后一项总是比前一项增加 a a a 或者减少 b b b 的整数数列可能有多少种呢?
输入的第一行包含四个整数 n , s , a , b n,s,a,b n,s,a,b,含义如前面说述。
输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以 100000007 100000007 100000007 的余数。
4 10 2 3
2
【样例说明】
这两个数列分别是 2 4 1 3 和 7 4 1 -2。
【数据规模与约定】
对于 10 % 10\% 10% 的数据, 1 ≤ n ≤ 5 1 \le n \le 5 1≤n≤5, 0 ≤ s ≤ 5 0 \le s \le 5 0≤s≤5, 1 ≤ a , b ≤ 5 1 \le a,b \le 5 1≤a,b≤5;
对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 30 1 \le n \le 30 1≤n≤30, 0 ≤ s ≤ 30 0 \le s \le 30 0≤s≤30, 1 ≤ a , b ≤ 30 1 \le a,b \le 30 1≤a,b≤30;
对于 50 % 50\% 50% 的数据, 1 ≤ n ≤ 50 1 \le n \le 50 1≤n≤50, 0 ≤ s ≤ 50 0 \le s \le 50 0≤s≤50, 1 ≤ a , b ≤ 50 1 \le a,b \le 50 1≤a,b≤50;
对于 70 % 70\% 70% 的数据, 1 ≤ n ≤ 100 1 \le n \le 100 1≤n≤100, 0 ≤ s ≤ 500 0 \le s \le 500 0≤s≤500, 1 ≤ a , b ≤ 50 1 \le a,b \le 50 1≤a,b≤50;
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1000 1 \le n \le 1000 1≤n≤1000, − 1 0 9 ≤ s ≤ 1 0 9 -10^9 \le s \le 10^9 −109≤s≤109, 1 ≤ a , b ≤ 1 0 6 1 \le a,b \le 10^6 1≤a,b≤106。
时限 1 秒, 256M。蓝桥杯 2014 年第五届省赛
首先我们要知道这个题的问题问的是什么,问的是和为S的有多少种情况是成立的有两种情况 一种是+a一种是减b。
那下一步我们就要把这个和S给表示出来,怎么表示这个S呢,其实很简单
如果把+a和-b看成一种操作 T 那么这种操作一共有N种如果+a有Na种那么-b就有N-Na种;
那T操作可能是+a或者是-b那显然,T操作一共可能是1+2+…+n-1的和:就是一个等差数列。
显然T操作会出现n*(n-1)/2次。
如果设+a会出现na次那-b就会出现n*(n-1)/2-na次(根据等差数列求和公式)
那S也可以简化为
可以看到上式子S与X有关,可以转变为有多少种的X可以等于S。
那我们可以看到 na 也是一个不确定的量,那可以用dp来解决,设dp[i][j]表示用1~n-1选择前i个数使其和为j的方案数,这样我们就能确定不同的na有多少方案来组成他。
可以得到状态转移方程为
dp[i][j] = dp[i-1][j]+dp[i-1][j-i] (可以选可以不选)
使用滚动数组可以压缩为:
dp[j] = (dp[j] + dp[j-i])%MOD;
那么我们的dp如下:
dp[0] = 1;
for (int i=1;i<n;i++)
{
for (int j=i*(i+1)/2;j>=i;j--)
{
dp[j] = (dp[j] + dp[j-i])%MOD;
}
}
这样我们就得到了和为na的方案数
na的方案数有了,na每个方案数的和我们也知道了,那么循环na就可以解方程了
循环na就能解方程了0到n*(n-1)/2
下面我们用y表示的na
int ans = 0;
for (int y=0; y<=temp; y++)
{
long long tt =s+b*temp-(a+b)*y;
if (tt%n==0)//方程有结果
{
ans = (ans + dp[y])%MOD;//就加上方案数
}
}
#include
const int maxn 1000010;
const int MOD 100000007;
int dp[maxn];
int main()
{
long long n,s,a,b;
cin>>n>>s>>a>>b;
int temp = n*(n-1)/2;
dp[0] = 1;
int i,j;
for (i=1;i<n;i++)
{
for (j=i*(i+1)/2;j>=i;j--)
{
dp[j] = (dp[j] + dp[j-i])%MOD;
}
}
int ans = 0;
for (int y=0; y<=temp; y++)
{
long long tt =s+b*temp-(a+b)*y;
if (tt%n==0)
{
ans = (ans + dp[y])%MOD;
}
}
cout<<ans;
return 0;
}
要注意题目问题的转换什么不变什么变,不要那道问题直接就出结果,过程可能也是用dp的关键,注意问题的深入和转化