AcWing 1214.波动数列

题目描述

原题链接:1214.波动数列

解题思路

1.分析时间复杂度

在题目中:1 ≤ n ≤ 1000 \le n \le 1000 n1000 n 2 = 1 0 6 < 1 0 8 n^2=10^6 \lt 10^8 n2=106<108,因此本题的时间复杂度差不多 o ( n 2 ) o(n^2) o(n2)

2.数学推导

假设数列的第一个数为 x x x,第 i i i次操作的数为 d i d_i di d i = + a d_i=+a di=+a − b -b b
那么一个长度为 n n n的数列就是 x , x + d 1 , x + d 1 + d 2 , . . . , x + d 1 + d 2 + . . . + d n − 1 x,x+d_1,x+d_1+d_2,...,x+d_1+d_2+...+d_{n-1} x,x+d1,x+d1+d2,...,x+d1+d2+...+dn1
这一串数列的和就是 n x + ( n − 1 ) d 1 + ( n − 2 ) d 2 + . . . + d n − 1 = s nx+(n-1)d_1+(n-2)d_2+...+d_{n-1}=s nx+(n1)d1+(n2)d2+...+dn1=s
进行变换后可得, x = s − ( n − 1 ) d 1 − ( n − 2 ) d 2 − . . . − d n − 1 n x=\frac{s-(n-1)d_1-(n-2)d_2-...-d_{n-1}}{n} x=ns(n1)d1(n2)d2...dn1
因为 x x x是整数,所以 s − ( n − 1 ) d 1 − ( n − 2 ) d 2 − . . . − d n − 1 s-(n-1)d_1-(n-2)d_2-...-d_{n-1} s(n1)d1(n2)d2...dn1一定是 n n n的倍数,即 s s s ( n − 1 ) d 1 + ( n − 2 ) d 2 + . . . + d n − 1 (n-1)d_1+(n-2)d_2+...+d_{n-1} (n1)d1+(n2)d2+...+dn1 n n n同余。这其实是一个组合问题,在 n − 1 n-1 n1个操作下,如何组合使得数列之和与 s s s同余。

3.闫氏dp分析

假设f[i][j]表示在考虑第 i i i次操作下,即第 i i i次操作 + a +a +a − b -b b,前 i + 1 i+1 i+1个数模 n n n结果为 j j j的方案数。
假如第 i i i次操作是 + a +a +a,那么前 i + 1 i+1 i+1个数字的和是 x + ( n − 1 ) d 1 + ( n − 2 ) d 2 + . . . + ( n − ( i − 1 ) ) d i − 1 + ( n − i ) a ≡ j ( m o d   n ) x+(n-1)d_1+(n-2)d_2+...+(n-(i-1))d_{i-1}+(n-i)a \equiv j(mod \ n) x+(n1)d1+(n2)d2+...+(n(i1))di1+(ni)aj(mod n)
那么, x + ( n − 1 ) d 1 + ( n − 2 ) d 2 + . . + ( n − ( i − 1 ) d i − 1 ≡ j − ( n − i ) a   ( m o d   n ) x+(n-1)d_1+(n-2)d_2+..+(n-(i-1)d_{i-1} \equiv j-(n-i)a \ (mod \ n) x+(n1)d1+(n2)d2+..+(n(i1)di1j(ni)a (mod n)。上述左端的式子刚好为考虑 i − 1 i-1 i1次操作之后数列的和。
通过以上的分析我们可以知道,当第 i i i次操作为 + a +a +a时,方案数为f[i-1][j-(n-1)*a]。同理,当第 i i i次操作为 − b -b b时,方案数为f[i-1][j+(n-1)*b]
f[i][j] = f[i-1][j-(n-1)*a] + f[i-1][j+(n-1)*b]
闫氏dp分析

4.一些注意点

(1)由于 j − ( n − 1 ) ∗ a j-(n-1)*a j(n1)a s s s都有可能是负数,而数组下标只能大于等于0,因此在取模的时候要注意是结果必须大于等于0。
(2)关于初始化的问题,当 i = 0 i=0 i=0时,满足题意的方案只有一种,就是 x = s x=s x=s,因此f[0][0] = 1

c++代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
const int P = 1e8+7;
int n,s,a,b,f[N][N];
int get_mod(int a, int b)
{
    return (a % b + b) % b; 
}
int main()
{
    cin >> n >> s >> a >> b;
    f[0][0] = 1;
    for(int i = 1; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            f[i][j] = (f[i-1][get_mod(j-(n-i)*a, n)] + f[i-1][get_mod(j+(n-i)*b, n)]) % P;
        }
    }
    cout << f[n-1][get_mod(s,n)];
}

参考文献


https://www.acwing.com/solution/content/9223/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值