数位dp

数位dp大致解决的是一类数字问题:从l到r有多少个数符合某个性质

设立前缀的状态时,一般有前缀的状态为:上一位的值,前缀的数字和,前缀所有数字的gcd,该前缀取模某个数的余数等等

 

例题:洛谷 P2657

f[i][j]表示数字位数为i,前缀的状态为j的windy数数量

递归版

#include <bits/stdc++.h>
#define debug frelimiten("r.txt","r",stdin)
#define mp make_pair
#define ri register int
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e3+5;
const int INF = 0x3f3f3f3f; 
const int mod = 998244353;
inline ll read(){ll s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;}
ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
int f[maxn][maxn],digit[maxn],T;
ll n,m;
//f数组表示当前将要考虑的是从高到低的第i位(这个位要取0~9任意无限制的数),当前该前缀的状态为st时的数字个数 
int dfs(int x,int st,bool limit) //x:第x位(从高位到低位) st:上一位数字(11则表示为前导0)  limit:上一位是否达到上限
{
    if (x==0) return 1;
    if (!limit && f[x][st]!=-1) return f[x][st];
    int up=(limit?digit[x]:9);
    int ans=0;
    for (ri i=0;i<=up;i++)
      {
        if (abs(st-i)<2) continue;
        if (st==11 && i==0)
            ans+=dfs(x-1,11,limit&&(i==up));
        else
              ans+=dfs(x-1,i,limit&&(i==up));
      }
      if (!limit) f[x][st]=ans;
      return ans;
}
int solve(int x) 
{
    memset(f,-1,sizeof(f));
    int len=0;
    for(;x;x/=10)
    {
        digit[++len]=x%10;
    }
    return dfs(len,11,true);   
}
int main()
{
    T=1;
    while (T--)
    {
        n=read(),m=read();
        cout<<solve(m)-solve(n-1)<<endl;       
    }
    return 0;
}
View Code

 

非递归版

#include <bits/stdc++.h>
#define debug frelimiten("r.txt","r",stdin)
#define mp make_pair
#define ri register int
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 5e3+5;
const int INF = 0x3f3f3f3f; 
const int mod = 998244353;
const int N=10005,M=4000005;
inline ll read(){ll s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;}
ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
int p,q,dp[15][15],digit[15];
void init()
{
    for (ri i=0;i<=9;i++) dp[1][i]=1;
    for (ri i=2;i<=10;i++) 
        for (ri j=0;j<=9;j++)
            for (ri k=0;k<=9;k++)
                if (abs(j-k)>=2) dp[i][j]+=dp[i-1][k];
}
int solve(int x)
{
    memset(digit,0,sizeof(digit));
    int len=0,ans=0;
    while (x)
    {
        digit[++len]=x%10;
        x/=10;
    }
    /*
        000x
        00xx
        0xxx...
    */
    //求len-1位的windy数 必定包含在区间里的 
    for (ri i=1;i<len;i++)
        for (ri j=1;j<=9;j++)
            ans+=dp[i][j];
    /*
        1xxx
        2xxx
        3xxx
        .
        .
        (a-1)xxxx
    */
    //然后是len位 但最高位<digit[len]的windy数 也包含在区间里 
    for (ri i=1;i<digit[len];i++)
        ans+=dp[len][i];
    /*
    a [0,b)[0,c)[0,d)...
    */
    for (ri i=len-1;i;i--)
    {
        for (ri j=0;j<digit[i];j++)
            if (abs(j-digit[i+1])>=2) ans+=dp[i][j];
        if (abs(digit[i+1]-digit[i])<2) break;
        if(i==1) ans+=1;
    }
    return ans;
}
int main()
{
    init();
    cin>>p>>q;
    cout<<solve(q)-solve(p-1)<<endl;
    return 0;
}
View Code

 

posted @ 2020-05-10 16:15  Y-KnightQin  阅读(163)  评论(0编辑  收藏  举报