被3整除的子序列

被3整除的子序列

image.png

思路

判断被\(3\)整除

当一个数的数位和为\(3\)的倍数时,该数就能被\(3\)整除。

证明

一个十进制数可以表示为\(a_1*10^{b_1}+a_2*10^{b_2}+...+a_n*10^{b_n}\)的形式。
通过,\((a*b) \,\ mod \,\ 3 = [(a \,\ mod \,\ 3) * (b \,\ mod \,\ 3)] \,\ mod \,\ 3\),再通过\(10 \,\ mod \,\ 3 = 1\),可以推出\(10^{x} \,\ mod \,\ 3 = 1\)
因此,十进制数的每一个项都可以转化为以下形式:
\(a_i*10^{b_i} \,\ mod \,\ 3 = [(a_i \,\ mod \,\ 3) * (10^{b_i} \,\ mod \,\ 3)] \,\ mod \,\ 3 = [(a_i \,\ mod \,\ 3) *1] \,\ mod \,\ 3 = a_i \,\ mod \,\ 3\)
那么,\(a_1*10^{b_1}+a_2*10^{b_2}+...+a_n*10^{b_n} = (a_1 + a_2 + ... + a_n) \,\ mod \,\ 3\),就成立了。
当这个式子结果为\(0\)时,说明对应的十进制数就能被\(3\)整除。

动态规划

状态表示

\(f[i][j]\)在前\(i\)个数中选数,构成的子序列的数位和对\(3\)取模后为\(j\)的所有方案

状态计算

设第\(i\)个数为\(k\)
(1)单独取第\(i\)个数,\(f[i][k\%3]\)方案数加一
(2)将第\(i\)个数拼接在前\(i-1\)个数中选出的子序列后面,构成的子序列数位和对\(3\)取模后为\(j\)
相当于选择前\(i-1\)个数中选出的子序列,其数位和为\(p\)\((p+k) \%3=j\),则\(p = (j-k+3)\%3\)
对应\(f[i-1][p]\)
(3)不选第\(i\)个数,子序列数位和对\(3\)取模为\(j\),对应\(f[i-1][j]\)
因此,\(f[i][j] = f[i-1][j]+f[i-1][p]\),如果\(k\%3=j\)\(f[i][j]\)还需要加一。

最后的答案即为\(f[n][0]\),前\(n\)个数中选数,构成的子序列的数位和对\(3\)取模后为\(0\),即能被\(3\)整除。

代码实现

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

const int N = 1e2;

const int mod = 1e9+7;

int f[N][3];
char str[N];

int main()
{
    cin>>str+1;
    int n = strlen(str+1);
    for(int i=1;i<=n;i++)
    {
        int k = (str[i]-'0')%3;
        f[i][k] = (f[i][k]+1)%mod;
        for(int j=0;j<3;j++)
        {
            f[i][j] = (f[i][j]+f[i-1][j])%mod;
            int p = (j-k+3)%3;
            f[i][j] = (f[i][j]+f[i-1][p])%mod;
        }
    }
    cout<<f[n][0];
    
    return 0;
}
posted @ 2023-03-16 23:01  烟尘墨  阅读(144)  评论(0)    收藏  举报