Newcoder 被三整除的子序列(基本状态转移)

这篇博客探讨了如何解决寻找一个长度为50的数字串中能被3整除的子序列数量的问题。通过分析不同数字模3的余数,博主提出了一个状态转移方程来描述每一步的影响。具体来说,当遇到模3为0的数字时,所有模3的方案数都会翻倍并加一;模3为1或2的数字会根据之前模3的余数来调整方案数。最终,这些规律用于构建解决方案,并对答案取模1e9+7。

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

给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除
答案对1e9+7取模

被三整除即各位数的和可以被三整除。

显然每选择一个数所造成的结果都会从原来已知的结果加上一些影响而得到,这里可以找几个数发现规律:

1563:当只有1的时候不存在被3整除的情况,只有15的时候存在一种被3整除的情况(15),只有156的时候存在三种被三整除的情况(6,15,156),1563的时候存在7种被3整除的情况(15,63,3,6,156,1563,153).

111,只有111的时候才有一种合法情况。

可以知道,三个取余3得1的数加起来一定是3的倍数,同样一个取余3得2的数和一个取余3得1的数合并一定是3的倍数。

以此类推,所以就有这样的状态转移方程:

  • 如果当前位取余3为0:所有组合的模(0,1,2)和此位组合都维持原状,且都可以组成一个同模的数字(1->13取余3仍然是1,但是取余3得1的方案数为原来的两倍),所以各种取余方案数全部乘2,之后取余3为0的方案数单独加一(单独选此位)。
  • 如果当前位取余3为1:对于取余3为0的方案来说,总的方案数为原本取余3就是0的方案数(不选这一位)加上取余3为2的方案数(取余3为2加上一个取余3为1的数模3为0)之和。同样,模3=1的方案可以由模3=0(加这一位)的情况加上模3=1(不加这一位)的情况构成,模3=2的方案同理
  • 取余3为2的情况如上所述可推得。

 

import java.util.*;
import java.math.*;
import java.io.*;
import java.text.*;
public class Main5
{
    static final int mod=1000000007;
    static int status[][]=new int[500][500];
    public static void main(String args[])throws IOException
    {
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        String tmp=bf.readLine();
        int limit=tmp.length();
        for(int current=1;current<=limit;current++)
        {
            int currentValue=Integer.valueOf(tmp.substring(current-1,current));
            if(currentValue%3==0)
            {
                status[current][0]=2*status[current-1][0]+1;
                status[current][1]=2*status[current-1][1];
                status[current][2]=2*status[current-1][2];
            }
            else if(currentValue%3==1)
            {
                status[current][0]=status[current-1][0]+status[current-1][2];
                status[current][1]=status[current-1][0]+status[current-1][1]+1;
                status[current][2]=status[current-1][2]+status[current-1][1];
            }
            else
            {
                status[current][0]=status[current-1][0]+status[current-1][1];
                status[current][1]=status[current-1][1]+status[current-1][2];
                status[current][2]=status[current-1][2]+status[current-1][0]+1;
            }
            for(int j=0;j<=2;j++)
                status[current][j]%=mod;
        }
        System.out.println(status[limit][0]);
        bf.close();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值