数位DP——Luogu3413 SAC#1 - 萌数

题面:Luogu3413
这好像是我写的第一篇数位DP的blog吧。。。
半原创吧,参考的是我的同学lc233的blog:传送门
首先看到这个数据范围和样例就可以知道这题的主体思路了吧。
我们可以定义状态 f[i][j][k] 表示位数为i的,最高位为j,次高位为k的萌数个数。
一开始推的话很简单:

首先回文串嘛>2就好啦
所以我们只要判断2情况
aa或者aba; ——lc233

所以我们只要考虑连续两位的问题就好了。
所以接下来考虑统计答案的问题了。我们发现这个状态会有很多重复,很麻烦。
那么我们把 f[i][j][k] 定义为不是萌数的个数,这就好办多了。
接下来就是常规数位DP的写法了。。。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
#define int long long
using namespace std;
const int MOD=1e9+7;
int f[1010][10][10];//注意这里f定义:这些数中不是萌数的个数,别搞错
string l,r;
inline int dp(string x){
    int l=x.size(),n=0,ans=0;
    for(int i=0;i<l;i++)n=(n*10+x[i]-'0')%MOD;n=(n==MOD)?0:n+1;
    for(int i=2;i<l;i++)
        for(int j=1;j<10;j++)
            for(int k=0;k<10;k++)(ans+=f[i][j][k])%=MOD;
    if(l>1)(ans+=10)%=MOD;//0~9
    int la=-1,lla=-1;bool flag=1;
    for(int i=0;i<l-1;i++){
        int now=x[i]-'0';
        for(int j=0;j<now;j++)if(i!=0||j!=0)
            for(int k=0;k<10;k++)if(la!=j&&lla!=j&&j!=k&&k!=la)(ans+=f[l-i][j][k])%=MOD;
        if(now==la||now==lla){flag=0;break;}
        lla=la;la=now;
    }
    if(flag)for(int j=0;j<=x[l-1]-'0';j++)if(j!=la&&j!=lla)ans=(ans==MOD)?0:ans+1;
    return (n-ans+MOD)%MOD;
}//整个统计答案过程不多解释了
signed main()
{
    ios::sync_with_stdio(0);
    for(int i=2;i<=1000;i++)
        for(int j=0;j<10;j++)
            for(int k=0;k<10;k++)if(j!=k){
                for(int l=0;l<10;l++)if(j!=l&&k!=l)(f[i][j][k]+=f[i-1][k][l])%=MOD;//如果没有出现回文现象加上去
                if(i-1==1)f[i][j][k]=(f[i][j][k]==MOD)?0:f[i][j][k]+1;//从1开始
            }
    cin>>l>>r;
    int ans=(dp(r)-dp(l)+MOD)%MOD;int L=l.size();
    for(int i=1;i<L;i++)if(l[i]==l[i-1]||i>1&&l[i]==l[i-2]){ans++;break;}//上面的差分是l+1~r的,这里把l加上去
    cout<<ans<<endl;
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值