洛谷 P3413 SAC#1 - 萌数

Description

洛谷传送门

Solution

典型的数位dp

题目要求求区间 \([l, r]\) 内有多少个数至少有长度为 2 的回文串。

不难发现,我们只需要考虑长度为 2 或 3 的回文串即可。

所以我们记忆化搜索时,存一下当前数的前一个数,和前前个数,判断一下即可。

具体看代码吧,感觉挺好理解的。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 1010
#define ll long long

using namespace std;

const ll mod = 1e9 + 7;
ll l1, l2, l, r;
ll num[N], dp[N][10][10];
char s1[N], s2[N];

void init(){
    scanf("%s%s", s1 + 1, s2 + 1);
    l1 = strlen(s1 + 1);
    l2 = strlen(s2 + 1);
    for(ll i = 1; i <= l1; i++)
        l = ((l * 10) % mod + s1[i] - '0') % mod;
    for(ll i = 1; i <= l2; i++)
        r = ((r * 10) % mod + s2[i] - '0') % mod;
    return;
}

ll dfs(ll len, ll a, ll b, ll flag, ll lim){ //a 表示前一位, b 表示前前位, flag 判断前导0, lim 判断前一位是否到最高位
    if(!len) return 1;
    if(!flag && !lim && a != -1 && b != -1 && dp[len][a][b] != -1) return dp[len][a][b];
    ll x = lim ? num[len] : 9;
    ll res = 0;
    for(ll i = 0; i <= x; i++){
        if(i == a || i == b) continue;
        if(flag && !i) res = (res + dfs(len - 1, -1, -1, 1, lim && (i == x))) % mod;//是前导 0 的话,a 和 b 都赋值为 -1
        else res = (res + dfs(len - 1, i, a, 0, lim && (i == x))) % mod;
    }
    if (!flag && !lim && a != -1 && b != -1) dp[len][a][b] = res;
    return res;
}

ll solve(){
    memset(dp, -1, sizeof(dp));
    for(ll i = 1; i <= l1; i++)
        num[l1 - i + 1] = s1[i] - '0';
    ll ans1 = dfs(l1, -1, -1, 1, 1);//计算的是 1 ~ l 中非萌数个数
    ans1--;//假设 l 是萌数,1 ~ l-1 中非萌数个数 -1
    for(ll i = 1; i <= l2; i++)
        num[l2 - i + 1] = s2[i] - '0';
    ll ans2 = dfs(l2, -1, -1, 1, 1);//此时 ans 为 l+1 ~ r 中非萌数个数
    for(int i = 2; i <= l1; i++)//判断 l 是否为萌数,若 l 为萌数,则补回来,暴力判断
        if(s1[i] == s1[i - 1] || (s1[i] == s1[i - 2] && i - 2 >= 1)){
            ans1++;
            break;
        }
    return ans2 - ans1;
}

int main(){
    init();
    printf("%lld\n", ((r - l - solve() + 1) % mod + mod) % mod);//前缀和思想
}

End

posted @ 2021-10-06 22:55  xixike  阅读(27)  评论(0编辑  收藏  举报