题解 P3413 【SAC#1 - 萌数】

· · 题解

简单的数位DP(传反参数活该WA爆)

回文串的题都不会做Emmm。

看到题目首先懵逼的,回文串?这怎么存啊?这么转移啊?

然后,脑回路清奇的我(这是正常的想法吧??)就想到了是不是可以取补集,毕竟是数字。

一句话:正难则反

一个数如果前面包含了回文串,以后的状态也无法确定。

然后就想到了如何判断一个数完全不含回文串。

仔细思考一下,发现当一个数的任意一位都不和前两位的数字相同时,这个数就不含回文串。

手推了一下两个样例,发现好像没问题。

于是就打起来了。

f[pos][pre][gpre] 表示在pos位时前一位是pre前两位是gpre的非回文串数。

和普通的数位DP一样转移。

code过程中主要有以下几个问题+细节:

1.

100%数据有1000位,这显然爆了。但是没事,这还方便了,不用拆数位了。直接字符串读入。(不要忘了翻转过来!!!!)

2.

都知道数位DP中最后求答案还用了前缀和的思想。如下:

ans=sum[r]-sum[l-1]

那我们如何处理一个1000位的数字那?

对于 l-1 这个操作还要写个高精度减法吗?

事实是:不用!!!!

可以直接求出 sum[l]sum[r] 然后特殊判断一下 l 这个左边界合不合法就行了,不用再写高精了。

3.

由于取补集了吗,所以最后要拿r-l+1再减去非回文串数才是答案,可是r-l+1太大了。。。

又要写高精了吗emmm???

当然不用,可以直接对lr取模,然后相减,得出来的结果与(r-l)mod(1e9+7)一致。

4.

转移时注意一下前导0,不要让前导0成为合法的pre或gpre(就是不要用当前位去匹配前面不存在的数)。

举个栗子:100以内,放到第三位,若第二位为空(此时第二位应赋为-1),第三位的0也是合法解。

5.

一定不要传反参数(调了我一早上QAQ)。

还有不要忘开 long long

Think twice , code once.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const long long mod=1e9+7;
const long long maxn=1001;
using namespace std;
string l,r;
long long len[maxn];
long long f[maxn][10][10];
long long dfs(long long pos,long long pre,long long gpre,bool lim,bool zero)
{
    if(pos==0) return 1;
    if(f[pos][pre][gpre]!=-1&&!lim&&!zero&&pre!=-1&&gpre!=-1) return f[pos][pre][gpre];
    long long up=lim?len[pos]:9;
    long long ans=0;
    for(long long i=0;i<=up;i++)
    {
        if(i!=pre&&i!=gpre&&!zero)
            ans=(ans+dfs(pos-1,i,pre,lim&&i==len[pos],0))%mod;
        else if(zero)
            ans=(ans+dfs(pos-1,(i==0&&zero)?-1:i,-1,lim&&i==len[pos],i==0&&zero))%mod;
    }
    if(!lim&&!zero&&pre!=-1&&gpre!=-1) return f[pos][pre][gpre]=ans;
    return ans;
}
long long slove()
{
    long long s=l.length();
    for(long long i=0;i<s;i++)
        len[s-i]=l[i]-'0';
    long long ans1=dfs(s,-1,-1,1,1);
    s=r.length();
    for(long long i=0;i<s;i++)
        len[s-i]=r[i]-'0';
    long long ans2=dfs(s,-1,-1,1,1);
    ans1--;
    s=l.length();
    for(long long i=2;i<=s;i++)
        if(l[i]==l[i-1]||(l[i]==l[i-2]&&(i-2>=1)))
        {
            ans1++;
            break;
        }
    return (ans2-ans1)%mod;
}
int main()
{
    cin>>l>>r;
    memset(f,-1,sizeof f);
    long long numl=0,numr=0;
    long long s1=l.length(),s2=r.length();
    for(long long i=0;i<s1;i++)
        numl=(numl*10%mod+(l[i]^48))%mod;
    for(long long i=0;i<s2;i++)
        numr=(numr*10%mod+(r[i]^48))%mod;
    printf("%lld",((numr-numl-slove()+1)%mod+mod)%mod);
    return 0;
} 

后排安利个人博客:传送门