Codeforces 1105C (dp)

本文探讨了一种计数问题的解决策略,通过将问题转化为余数问题,并使用动态规划进行高效求解。具体地,文章分析了一个特定的数学问题,即在给定区间内寻找数组,使数组元素之和能被3整除。通过分析余数的分布规律,利用动态规划实现了O(n)的时间复杂度求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题意:

有一个长度为 n n n的数列的未知数列,数列的每一个数的值都在区间 [ l , r ] [l,r] [l,r]的范围内。现在问你能够构成多少个这样的数组,使得数组内的所有数的和能够被 3 3 3整除。

题目分析:

在这个题中,我们不能纠结在具体的数值的变化,我们需要关注数量的变化。

首先,涉及到这类整除性的问题,我们需要将它转化成余数的问题。那么我们可以发现,这些数的余数只会在 [ 0 , 2 ] [0,2] [0,2]的范围之间变化,因此我们只需分别考虑这三种情况。

我们考虑这样一个问题。如果一个数 x x x能够被 3 3 3整除,则我们设 x = 3 k x=3k x=3k。而因为 l ≤ x ≤ r l\le x \le r lxr,则我们有 l 3 ≤ k ≤ r 3 \frac{l}{3}\le k\le \frac{r}{3} 3lk3r。则易得,能被 3 3 3整除的数的个数为: r 3 − l 3 \frac{r}{3}-\frac{l}{3} 3r3l

同理有,模 3 3 3 1 1 1的个数为: r − 1 3 − l − 1 3 \frac{r-1}{3}-\frac{l-1}{3} 3r13l1

3 3 3 2 2 2的个数为: r − 2 3 − l − 2 3 \frac{r-2}{3}-\frac{l-2}{3} 3r23l2

得到个数之后,我们就可以用 d p dp dp对答案进行转移。

我们设 d p [ i ] [ j ] dp[i][j] dp[i][j]为数列的前 i i i个数字被取了后,余数为 j j j的方案数, c n t [ i ] cnt[i] cnt[i]为在区间 [ l , r ] [l,r] [l,r]中,模 3 3 3 i i i的个数。

则我们容易发现,当前的状态,是由前一个状态分别加上余 0 0 0,余 1 1 1,余 2 2 2的方案数转移过来的,即有状态转移方程: d p [ i ] [ j + k ] + = d p [ i − 1 ] [ j ] ∗ c n t [ k ] dp[i][j+k]+=dp[i-1][j]*cnt[k] dp[i][j+k]+=dp[i1][j]cnt[k]

因此我们可以用 O ( n ) \mathcal{O}(n) O(n)的时间复杂度进行转移,最终的答案即为 d p [ n ] [ 0 ] dp[n][0] dp[n][0]

#include <bits/stdc++.h>
#define maxn 200005

using namespace std;
typedef long long ll;
ll dp[maxn][3];
const int mod=1e9+7;
int main()
{
    int n,l,r;
    scanf("%d%d%d",&n,&l,&r);
    l--;
    dp[0][0]=1;
    for(int i=0;i<n;i++){
        for(int j=0;j<3;j++){
            ll w=(r-j+3)/3-(l-j+3)/3;
            for(int k=0;k<3;k++){
                dp[i+1][(k+j)%3]=(dp[i+1][(k+j)%3]+dp[i][k]*w)%mod;
            }
        }
    }
    cout<<dp[n][0]<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值