数位DP

入门题


HDU 2089 http://acm.hdu.edu.cn/showproblem.php?pid=2089


#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<map>
using namespace std;
typedef long long ll;
#define maxn 10
int dp[maxn][2];  //dp[0][i] 吉利数次 ,dp[1][i] 末尾是6的吉利数字<=>开头时2的吉利数字

void get_dp(){
    dp[0][0]=1;
    dp[0][1]=0;
    dp[1][0]=9;
    dp[1][1]=1;
    for(int i=2;i<maxn;i++){
        dp[i][0]=dp[i-1][0]*9-dp[i-1][1];
        dp[i][1]=dp[i-1][0]+dp[i][1];
    }
}

int solve(int n){
    if(n<0) return 0;
    
    int q[maxn],lq=0,ans=0;
    while(n>0){q[lq++]=n%10;n/=10;}
    q[lq]=0;
    bool mark=1;
    for(int i=lq-1;i>=0;i--){
        if(!mark) continue;
        
        if(q[i]==4||(q[i+1]==6&&q[i]==2)) mark=0;
        
        if(q[i]<5) ans+=q[i]*dp[i][0];              //  axxx
        else if(q[i]<7) ans+=(q[i]-1)*dp[i][0];     //  axxx-4xxx
        else ans+=(q[i]-1)*dp[i][0]-dp[i][1];       //  axxx-4xxx-62xx
        
        if(q[i+1]==6&&q[i]>2) ans-=dp[i][0];        // -2xxx
    }
    if(mark) ans++;
    return ans;
}
int main(){
    int l,r;
    get_dp();
    while(scanf("%d%d",&l,&r),l!=0||r!=0){
        printf("%d\n",solve(r)-solve(l-1));
    }
}



HDU 3555 http://acm.hdu.edu.cn/showproblem.php?pid=3555


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define maxn 20
typedef long long ll;
ll dp[maxn][2]; //dp[i][0] i位有几个不包含49的  dp[i][1] 9 为首位的不含49的数量
void get_dp(){
    dp[0][0]=1;
    dp[0][1]=0;
    dp[1][0]=10;
    dp[1][1]=1;
    for(int i=2;i<maxn;i++){
        dp[i][0]=dp[i-1][0]*10-dp[i-1][1];
        dp[i][1]=dp[i-1][0];
    }
}

ll solve(ll n){
    int p[maxn],lp=0;
    ll ans=0;
    while(n>0){
        p[lp++]=n%10;
        n/=10;
    }
    p[lp]=0;
    bool mark=1;
    for(int i=lp-1;i>=0;i--){
        if(!mark) continue;
        if(p[i+1]==4&&p[i]==9) mark=0;
        if(p[i]<5) ans+=p[i]*dp[i][0];
        else ans+=p[i]*dp[i][0]-dp[i][1];
    }
    if(mark) ans++;
    return ans;
}
int main(){
    get_dp();
    int t;
    ll n;
    scanf("%d",&t);
    while(t--){
        scanf("%I64d",&n);
        printf("%I64d\n",n+1-solve(n));
    }
}



HDU 3652 http://acm.hdu.edu.cn/showproblem.php?pid=3652


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define maxn 20
typedef long long ll;
#define A 11   //位数
#define B 13   //mod 13 的余数
#define C 3    // 0不含有13 ,1 含有 13 的 ,2 首位是3不含有13   xxxxx  3xxxx
int dp[A][B][C];
void get_dp(){
    memset(dp,0,sizeof(dp));
    dp[0][0][0]=1;
    for(int i=0;i<10;i++)
        dp[1][i][0]=1;
    dp[1][3][2]=1;
    int pul=1;
    for(int i=2;i<=A;i++){
        pul*=10;
        for(int j=0;j<B;j++){
            for(int k=0;k<10;k++){
                dp[i][(j+k*pul)%B][0]+=dp[i-1][j][0];
                dp[i][(j+k*pul)%B][1]+=dp[i-1][j][1];
            }
            dp[i][(j+pul)%B][0]-=dp[i-1][j][2];
            dp[i][(j+pul)%B][1]+=dp[i-1][j][2];
            dp[i][(j+3*pul)%B][2]+=dp[i-1][j][0];
        }
    }
}

int solve(int n){
    if(n<=0) return 0;
    int q[A],lq=0,ans=0,pul=1;
    
    while(n>0){
        q[lq++]=n%10;
        n/=10;
        pul*=10;
    }
    
    pul/=10;
    q[lq]=0;
    bool mark=0;
    int mod=0;
    for(int i=lq-1;i>=0;i--){
        for(int j=0;j<q[i];j++){
            for(int k=0;k<B;k++){
                if((mod+j*pul+k*(pul/10))%B==0){
                    ans+=dp[i][(k*(pul/10))%B][1];
                    
                    if(mark) ans+=dp[i][(k*(pul/10))%B][0]; // 之前有13
                    
                    else if(q[i+1]==1){
                        if(j==3) ans+=dp[i][(k*(pul/10))%B][0];//前一位是1
                        else if(j==1) ans+=dp[i][(k*(pul/10))%B][2];
                    }
                    
                    else{                                     //其他
                        if(j==1) ans+=dp[i][(k*(pul/10))%B][2];
                    }
                }
                if(i==0) break;
            }
        }
        
        if(q[i+1]==1&&q[i]==3)
            mark=1;
        mod=(mod+q[i]*pul)%B;
        pul/=10;
        
    }
    if(mark&&mod==0) ans++;
    return  ans;
}
int main(){
    get_dp();
    int n;
    while(scanf("%d",&n)!=EOF)
        printf("%d\n",solve(n));
}



HDU 3709 http://acm.hdu.edu.cn/showproblem.php?pid=3709

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<map>
using namespace std;
typedef long long ll;
#define maxn 10
#define A 20
#define B 20

#define C 4000
ll dp[A][B][C]; // 平衡位置,第几位,差值
void get_dp(){
    memset(dp,0,sizeof(dp));
    for(int i=0;i<A;i++) dp[i][0][C/2]=1; dp[1][0][C/2]=1;
    for(int i=0;i<A;i++)
        for(int j=0;j<B-1;j++)
            for(int k=0;k<C;k++){
                if(dp[i][j][k]==0)
                    continue;
                for(int p=0;p<10;p++)
                    dp[i][j+1][k+p*(j+1-i)]+=dp[i][j][k];
            }
}

ll solve(ll n){
    if(n<0) return 0;
    if(n==0) return 1;
    int q[B],lq=0;
    ll ans=0;
    while(n>0){
        q[lq++]=n%10;
        n/=10;
    }
    int cas[A]; memset(cas,0,sizeof(cas));
    for(int i=lq-1;i>=0;i--){ //第几位
        for(int j=1;j<A;j++){ // 平衡位置在哪
                for(int k=0;k<q[i];k++) // 小于它的数
                {
                    ans+=dp[j][i][C/2-cas[j]-(i+1-j)*k];
                }
        }
        for(int j=0;j<A;j++)
             cas[j]+=(i+1-j)*q[i];
    }
    for(int i=0;i<A;i++)
        if(cas[i]==0) ans++;
    ans=ans-A+2;
    return ans;
    
}


int main(){
    int t;
    scanf("%d",&t);
    ll l,r;
    get_dp();
    while(t--){
        scanf("%I64d%I64d",&l,&r);
        printf("%I64d\n",solve(r)-solve(l-1));
    }
}



HDU 3943 http://acm.hdu.edu.cn/showproblem.php?pid=3943

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<map>
using namespace std;
typedef long long ll;
#define maxn 21
ll dp[20][maxn][maxn];
void get_dp(){
    memset(dp,0,sizeof(dp));
    dp[0][0][0]=1;
    for(int i=1;i<20;i++)
        for(int j=0;j<maxn;j++)
            for(int k=0;k<maxn;k++){
                
                dp[i][j][k]+=dp[i-1][j][k]*8;// dp[2][1][0]=dp[1][1][0]*10+dp[1][0][0];
                
                if(j!=0) dp[i][j][k]+=dp[i-1][j-1][k];
              
                if(k!=0) dp[i][j][k]+=dp[i-1][j][k-1];
                
            }
}

ll solve(int a,int b,ll n){
    int q[maxn],lq=0;
    ll ans=0;
    while(n>0){
        q[lq++]=n%10;
        n/=10;
    }
    for(int i=lq-1;i>=0;i--){
        for(int j=0;j<q[i];j++){
            if(j==4) {if(a!=0) ans+=dp[i][a-1][b];}
            else if(j==7) {if(b!=0) ans+=dp[i][a][b-1];}
            else ans+=dp[i][a][b];
        }
        if(q[i]==4) a--;
        if(q[i]==7) b--;
        if(a<0||b<0) break;
    }
    if(a==0&&b==0) ans++;
    return ans;
}
int main(){
    int t,cas=1;
    scanf("%d",&t);
    int x,y;
    ll p,q;
    get_dp();
    while(t--){
        scanf("%I64d%I64d%d%d",&p,&q,&x,&y);
        ll n1=solve(x,y,p);
        ll n2=solve(x,y,q);
        int m;
        scanf("%d",&m);
        printf("Case #%d:\n",cas++);
        while(m--){
            ll n;
            scanf("%I64d",&n);
            if(n>n2-n1){printf("Nya!\n"); continue;}
            n=n+n1;
            ll st=p,nd=q;
            while(st<=nd){
                ll mid=(st>>1)+(nd>>1);
                if(st%2==1&&nd%2==1) mid++;
                ll temn=solve(x,y,mid);
                if(temn>=n) nd=mid-1;
                else st=mid+1;
            }
            printf("%I64d\n",st);
        }
    }
}




HDU 4389  http://acm.hdu.edu.cn/showproblem.php?pid=4389

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<map>
using namespace std;
typedef long long ll;
#define A 11
#define B 91
#define C 91
int dp[A][B][C][C];  // 位 和 模数 余数

void get_dp(){
    memset(dp,0,sizeof(dp));
    for(int i=0;i<C;i++)
        dp[0][0][i][0]=1;
    for(int i=0;i<A-1;i++){
        for(int j=0;j<=i*9;j++){
            for(int k=0;k<C;k++){
                for(int p=0;p<k||p==0;p++){
                    if(dp[i][j][k][p]==0) continue;
                    for(int t=0;t<10;t++){
                        int mod;
                        if(k==0) mod=0;
                        else mod=(p*10+t)%k;
                        dp[i+1][j+t][k][mod]+=dp[i][j][k][p];
                    }
                }
            }
        }
    }
}

int solve(int n){
    n++;
    int q[A],lq=0;
    int ans=0;
    int pul=1;
    while(n>0){
        q[lq++]=n%10;
        n/=10;
        pul*=10;
    }
    int sum=0;
    int ssum=0;
    for(int i=lq-1;i>=0;i--){
        for(int j=0;j<=i*9;j++){
            for(int k=0;k<q[i];k++){
                int mod;
                int J=j+k+ssum;
                if(J) mod=(J-(k*(pul/10)+sum*pul)%J)%J;
                else mod=0;
                ans+=dp[i][j][J][mod];
            }
        }
        sum=sum*10+q[i];
        ssum+=q[i];
        pul/=10;
    }
    return ans;
}

int main(){
    int t,cas=1;
    scanf("%d",&t);
    int l,r;
    get_dp();
    while(t--){
        scanf("%d%d",&l,&r);
        printf("Case %d: %d\n",cas++,solve(r)-solve(l-1));
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值