Luogu P2602 [ZJOI2010]数字计数 数位DP

很久以前就。。。但是一直咕咕咕


思路:数位$DP$

提交:1次

题解:见代码

#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
#define R register ll
using namespace std;
ll f[15][15],a,b;
//f[l][sum]对应dfs中(因为只在!ul&&!ck的时候记忆化)
int num[15];
ll dfs(int l,bool ul,bool ck,int lst,int sum) {//l剩余位数,ul上界标记,ck前导零标记,lst为所统计的数,sum是统计出现了几次合法的数字 
    if(!l) return sum;
    if(!ul&&!ck&&~f[l][sum]) return f[l][sum];//记忆化 
    R mx=(ul?num[l]:9),cnt=0;//判断上界 
    for(R i=0;i<=mx;++i) 
        cnt+=dfs(l-1,ul&&(i==mx),ck&&!i,lst,sum+((!ck||i)&&(i==lst)));//sum++,当且仅当不是一直是前导零或有数,同时是所统计的数 
    return ul||ck?cnt:f[l][sum]=cnt;//记忆化 
}
inline ll solve(ll x,int n) {
    R len=0; memset(f,0xff,sizeof(f));
    for(;x;x/=10) num[++len]=x%10;//按位分解 
    return dfs(len,true,true,n,0);
}
signed main() {
    scanf("%lld%lld",&a,&b);
    for(R i=0;i<=9;++i) printf("%lld ",solve(b,i)-solve(a-1,i));//前缀和 
    putchar('
');
}

#include<cstdio>
#include<iostream>
#include<cstring>
#define R register int
using namespace std;
int a,b,f[12][10],num[11];
//f[i][j]搜到第i位,前一位是j,且没有上界标记的方案数 
inline int max(int a,int b){return a>b?a:b;}
inline int abs(int x){return x>0?x:-x;}
int dfs(int l,bool ul,bool ck,int lst) {//l位数,ul上界标记,ck前导零标记,lst上一位 
    if(!l) return 1;
    if(!ul&&(~f[l][lst])) return f[l][lst];//记忆化 
    R mx=ul?num[l]:9,cnt=0;//mx是上界 
    for(R i=0;i<=mx;++i) {
        if(abs(lst-i)<2) continue;//差小于2 
        if(ck&&i==0) cnt+=dfs(l-1,ul&&i==mx,true,-2);//若一直是前导零 
        else cnt+=dfs(l-1,ul&&i==mx,false,i); 
    } return ul||ck?cnt:f[l][lst]=cnt;
}
inline int solve(int x) {
    R len=0; memset(f,0xff,sizeof(f));
    for(;x;x/=10) num[++len]=x%10;
    return dfs(len,true,true,-2);
}
signed main() {
    scanf("%d%d",&a,&b);
    printf("%d
",solve(b)-solve(a-1));//前缀和减一下 
}

2019.07.18