蓝桥杯每日一真题——[蓝桥杯 2014 省 A] 波动数列(数学转化,dp)

文章目录

  • [蓝桥杯 2014 省 A] 波动数列
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
      • 思路
      • 全部代码:
      • 总结:

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

题目描述

观察这个数列:

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 的余数。

样例 #1

样例输入 #1

4 10 2 3

样例输出 #1

2

提示

【样例说明】

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

【数据规模与约定】

对于 10 % 10\% 10% 的数据, 1 ≤ n ≤ 5 1 \le n \le 5 1n5 0 ≤ s ≤ 5 0 \le s \le 5 0s5 1 ≤ a , b ≤ 5 1 \le a,b \le 5 1a,b5

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 30 1 \le n \le 30 1n30 0 ≤ s ≤ 30 0 \le s \le 30 0s30 1 ≤ a , b ≤ 30 1 \le a,b \le 30 1a,b30

对于 50 % 50\% 50% 的数据, 1 ≤ n ≤ 50 1 \le n \le 50 1n50 0 ≤ s ≤ 50 0 \le s \le 50 0s50 1 ≤ a , b ≤ 50 1 \le a,b \le 50 1a,b50

对于 70 % 70\% 70% 的数据, 1 ≤ n ≤ 100 1 \le n \le 100 1n100 0 ≤ s ≤ 500 0 \le s \le 500 0s500 1 ≤ a , b ≤ 50 1 \le a,b \le 50 1a,b50

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1000 1 \le n \le 1000 1n1000 − 1 0 9 ≤ s ≤ 1 0 9 -10^9 \le s \le 10^9 109s109 1 ≤ a , b ≤ 1 0 6 1 \le a,b \le 10^6 1a,b106

时限 1 秒, 256M。蓝桥杯 2014 年第五届省赛

思路

首先我们要知道这个题的问题问的是什么,问的是和为S的有多少种情况是成立的有两种情况 一种是+a一种是减b。

那下一步我们就要把这个和S给表示出来,怎么表示这个S呢,其实很简单
如果把+a和-b看成一种操作 T 那么这种操作一共有N种如果+a有Na种那么-b就有N-Na种;

那S如果要表达出来是什么呢?蓝桥杯每日一真题——[蓝桥杯 2014 省 A] 波动数列(数学转化,dp)_第1张图片

那T操作可能是+a或者是-b那显然,T操作一共可能是1+2+…+n-1的和:就是一个等差数列。

显然T操作会出现n*(n-1)/2次。

如果设+a会出现na次那-b就会出现n*(n-1)/2-na次(根据等差数列求和公式)

那S也可以简化为蓝桥杯每日一真题——[蓝桥杯 2014 省 A] 波动数列(数学转化,dp)_第2张图片
可以看到上式子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的关键,注意问题的深入和转化

你可能感兴趣的:(蓝桥杯,蓝桥杯,算法,职场和发展)