???牛客周赛55:小红的序列乘积2.0(dp)

题目描述

          \,\,\,\,\,\,\,\,\,\,对于一个长度为 mmm 的整数序列 {b1,b2,…,bm}\{b_1,b_2,\dots,b_m\}{b1​,b2​,…,bm​},定义 fi=b1×b2×⋯×bif_i = b_1 \times b_2 \times \cdots \times b_ifi​=b1​×b2​×⋯×bi​ ,即前 iii 项的乘积。这个序列的权值即为 f1,f2,…,fmf_1, f_2, \dots, f_mf1​,f2​,…,fm​ 中个位数是 666 的数字个数。

          \,\,\,\,\,\,\,\,\,\,小红有一个长度为 nnn 的整数序列 {a1,a2,…,an}\{a_1,a_2,\dots,a_n\}{a1​,a2​,…,an​},她想知道这个序列全部 2n−12^n-12n−1 个非空子序列的权值和是多少。

          \,\,\,\,\,\,\,\,\,\,如果序列 aaa 可以通过删除序列 bbb 中的若干(可能为零或全部)元素得到,则序列 aaa 是序列 bbb 的子序列

输入描述:

          \,\,\,\,\,\,\,\,\,\,第一行输入一个整数 n(1≤n≤105)n \left(1 \leq n \leq 10^5\right)n(1≤n≤105) 代表序列中元素的数量。
          \,\,\,\,\,\,\,\,\,\,第二行输入 nnn 个整数 a1,a2,⋯ ,an(1≤ai≤109)a_1, a_2, \cdots, a_n \left(1 \leq a_i \leq 10^9\right)a1​,a2​,⋯,an​(1≤ai​≤109) 代表序列元素。

输出描述:

          \,\,\,\,\,\,\,\,\,\,在一行上输出一个整数,输出一个整数,表示全部子序列的权值和。由于答案可能很大,请将答案对 (109+7)(10^9+7)(109+7) 取模后输出。

示例1

输入

复制3 4 4 6

3
4 4 6

输出

复制4

4

说明

          \,\,\,\,\,\,\,\,\,\,对于子序列 [4][4][4] ,没有贡献;
          \,\,\,\,\,\,\,\,\,\,对于子序列 [6][6][6] ,f={6}f=\{6\}f={6} 贡献为 111 ;
          \,\,\,\,\,\,\,\,\,\,对于子序列 [4,4][4,4][4,4] ,f={4,16}f=\{4,16\}f={4,16} 贡献为 111 ;
          \,\,\,\,\,\,\,\,\,\,对于子序列 [4,6][4,6][4,6] ,f={4,24}f=\{4,24\}f={4,24} ,没有贡献;
          \,\,\,\,\,\,\,\,\,\,对于子序列 [4,4,6][4,4,6][4,4,6] ,f={4,16,96}f=\{4,16,96\}f={4,16,96} 贡献为 222 。

示例2

输入

复制5 1 2 3 4 4

5
1 2 3 4 4

输出

复制12

12

做法

摘抄:

容易想到,只考虑前i个数的序列乘积的个位只有10种状态:个位为0~9

定义f[i][j]:前i个数中,序列乘积的个位为j的方案数

贡献:前i个数中,以a[i]结尾的序列且乘积个位为6能提供的贡献

这里与题意序列贡献有所不同,因为我们根据f[i][j]定义已知序列在前面的方案总数,我们只考虑当前序列作为序列的前缀还会在后面还会出现多少次,出现多少次就贡献了多少答案

结论:以a[i]结尾的序列作为序列的前缀还能在后面出现的次数为2^(n-i)(后面每个数有可选可不选两种抉择)

#include<bits/stdc++.h>
using namespace std;

int n;
long long a[100010];
long long dp[100010][12];
long long ans;
const long long mod=1e9+7;

long long ksm(long long a,long long b){
    long long ans=1;
    while(b){
        if(b%2) ans=ans*a%mod;
        b/=2;
        a=a*a%mod;
    }
    return ans;
}

int main(){

    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);

    dp[0][1]=1;

    for(int i=1;i<=n;i++){

        for(int j=0;j<=9;j++){

            dp[i][j*a[i]%10]+=dp[i-1][j];
            dp[i][j*a[i]%10]%=mod;
            
            dp[i][j]+=dp[i-1][j];
            dp[i][j]%=mod;

        }

    }
    
    for(int i=1;i<=n;i++){

        //负数情况也能处理???
        ans=(ans+(dp[i][6]-dp[i-1][6]+mod)%mod*ksm(2,n-i)%mod)%mod;

    }

    cout<<ans<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值