被3整除的子序列
思路
判断被\(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;
}
标签:10,int,个数,序列,整除,mod
From: https://www.cnblogs.com/ycm-zs/p/17224586.html