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

87 篇文章 0 订阅

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

题解

很显然,这是数位dp。
将问题变为求1~r的减去1~(l-1)的。

如果不出现回文超过1的串,其实只需要判断是否存在
长度为1或者2的就可以了。
fi,s1,s2
表示当前是第i位,不存在前导0,末尾两位分别是s1,s2。
转移就枚举一个k,表示之前的那一位选了什么。

code

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 150003
#define db double
#define P putchar
#define G getchar
#define mo 1000000007
using namespace std;
char ch;
void read(ll &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}

void write(ll x)
{
     if(x>9) write(x/10);
     P(x%10+'0');
}

ll a,b,ans;
ll f[23][13][13];

ll work(ll x)
{
    if(x<10)return x+1;
    ll t[23],len=0,g=1;
    while(x)
    {
        t[++len]=x%10;
        x/=10;
    }
    for(int i=1;i<=len/2;i++)
        swap(t[i],t[len-i+1]);
    t[0]=10;t[len+1]=-123;

    memset(f,0,sizeof(f));
    //f[1][0][0]=1;
    for(int i=1;i<=len;i++)
    {
        for(int s1=0;s1<10;s1++)
            for(int s2=0;s2<10;s2++)
            {
                if(s1==s2)continue;
                for(int k=0;k<=10;k++)
                    if(k!=s1 && k!=s2)f[i][s1][s2]+=f[i-1][k][s1];
            }

        /*if(i>1)
        {
            for(int k=0;k<10;k++)
                f[i][10][k]++;
        }*/

        for(int k=i==1;k<t[i];k++)
            if(t[i-1]!=k && t[max(i-2,0)]!=k)f[i][t[i-1]][k]+=g;
        if(t[i]==t[i-1] || t[i-2]==t[i])g=0;
    }
    ll ans=g;
    for(int i=0;i<10;i++)
        for(int j=0;j<10;j++)
            ans+=f[len][i][j];
    return ans;
}

ll sum(ll x)
{
    ll ans=work(x),t=10;
    x/=10;
    while(x>0)ans+=work(t-1),t*=10,x/=10;
    return ans;
}

int main()
{
    freopen("numbers.in","r",stdin);
    freopen("numbers.out","w",stdout);
    read(a);read(b);
    printf("%lld",sum(b)-sum(a-1));
    //while(1)read(a),write(sum(a)),P('\n');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值