JZOJ 5379. 【NOIP2017提高A组模拟9.21】Victor爱数字

Description

Victor 是一名热爱数字的同学。他最近在思考这样一个问题:
一个字符串是回文的当且仅当它倒过来还和原来相同。那么如果一个数的数串没有一个长度超过1 的子串是回文串的话,它就是palindrome-free 的。例如:16276 是palindrome-free的,而17276 不是,因为它包含了回文串727。
Victor 想知道在a 到b 的区间内,有多少个数是palindrome-free 的。

Input

从文件numbers.in 中读入数据。
包括两个数字a,b。

Output

输出到文件numbers.out 中。
输出包含一个整数:区间a,…,b 中palindrome-free 的数的总个数(包括a,b)。

Sample Input

输入1:

123 321

输入2:

123456789 987654321

Sample Output

输出1:

153

输出2:

167386971

Data Constraint

对于16% 的数据,b - a <= 200。
对于24% 的数据,b - a <= 10^5。
对于32% 的数据,b - a <= 10^6。
对于100% 的数据,0 <= a <= b <= 10^18

Solution

  • 这道题是一道典型的数位DP,即根据枚举数位来统计结果的动态规划。

  • 套路设 Work(x) 表示从 0 x 的答案,则答案即为 Work(b)Work(a1)

  • 因为回文只需判断长度为 2 3 的中心串即可,

  • 于是设出状态 F[i][j][k][l] 表示填了 i 位、 上两位为j l 限制越界0/1 的方案数。

  • 为方便转移,k 0 (第 1 i 为前导 0)、 1 (第 1 i1 为前导 0 ) 或 2 (其他情况)。

  • 答案即为 F[n][i][j][k]

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long n,m;
int a[20];
long long f[20][100][3][2];
inline long long work(long long x)
{
    for(a[0]=0;x;x/=10) a[++a[0]]=x%10;
    reverse(a+1,a+1+a[0]);
    memset(f,0,sizeof(f));
    f[0][0][0][1]=1;
    for(int i=0;i<a[0];i++)
        for(int j=0;j<=99;j++)
            for(int k=0;k<2;k++)
                if(k)
                {
                    for(int l=0;l<a[i+1];l++)
                    {
                        if(!l) f[i+1][j%10*10+l][0][0]+=f[i][j][0][1]; else
                            f[i+1][j%10*10+l][1][0]+=f[i][j][0][1];
                        if(j<=9 && j && j!=l) f[i+1][j%10*10+l][2][0]+=f[i][j][1][1];
                        if(j%10!=l && j/10!=l) f[i+1][j%10*10+l][2][0]+=f[i][j][2][1];
                    }
                    if(!a[i+1]) f[i+1][j%10*10+a[i+1]][0][1]+=f[i][j][0][1]; else
                        f[i+1][j%10*10+a[i+1]][1][1]+=f[i][j][0][1];
                    if(j<=9 && j && j!=a[i+1]) f[i+1][j%10*10+a[i+1]][2][1]+=f[i][j][1][1];
                    if(j%10!=a[i+1] && j/10!=a[i+1]) f[i+1][j%10*10+a[i+1]][2][1]+=f[i][j][2][1];
                }else
                    for(int l=0;l<=9;l++)
                    {
                        if(!l) f[i+1][j%10*10+l][0][0]+=f[i][j][0][0]; else
                            f[i+1][j%10*10+l][1][0]+=f[i][j][0][0];
                        if(j<=9 && j && j!=l) f[i+1][j%10*10+l][2][0]+=f[i][j][1][0];
                        if(j%10!=l && j/10!=l) f[i+1][j%10*10+l][2][0]+=f[i][j][2][0];
                    }
    long long sum=0;
    for(int i=0;i<=99;i++)
        for(int j=0;j<3;j++)
            for(int k=0;k<2;k++)
                sum+=f[a[0]][i][j][k];
    return sum;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    printf("%lld",work(m)-work(n-1));
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值