洛谷 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);//前缀和思想
}