数位dp——奏响数字数位的美妙乐章

数位dp:处理数字数位关系的一种dp方式。

一般的题目特征十分明显:

1.一般和数字本身有很大关系。

2.一般求数字在区间L,R中的一些信息

3.L,R一般很大,通常能达到long long级别。

dp方式也比较有套路:

一般有三种方法:

本质上的相似之处,都是集中在处理“填数有无限制”,“填数无限制情况下的固定方案数”,“某些已经搜出来的固定方案数”

1.记忆化搜索(没用过)

是一种倒着记忆的方法。

2.递推(我基本都是这个方法)

正着递推出答案。

一般会从高位向低位递推,讨论高位的填数方法,从而递推出低位的限制与否。

从高位到低位可以通过填的位数处理限制问题。

用一个[0/1]表示,填完这一位之后,是否对后面的数有限制。

填一个数,从k<num,k==num,k>num 讨论。

注意处理可能出现的前导零的锅。

3.预处理一些固定没有限制的一些位置,然后从高位开始填,

没有限制的话,直接查表。

有限制的话,继续填下一位。

注意处理已经填好的位置的固定影响。

经典入门例题:

[ZJOI2010]数字计数

给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

直接做就好了。

可以处理1~9,1~99,。。的固定答案,或者直接递推下去。

处理1~9,1~99,1~999...的方法:

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=20;
ll a,b;
ll cnta[20],cntb[20];
ll f[20],ten[20];
void query(ll x,ll *cnt)
{
    int len=0;
    int num[20];
    ll k=x;
    while(k)
    {
        num[++len]=k%10;
        k/=10;
    }
    for(int i=len;i>=1;i--)
    {
        for(int j=0;j<=9;j++)
         cnt[j]=cnt[j]+f[i-1]*num[i];
        for(int j=0;j<num[i];j++)
         cnt[j]+=ten[i-1];
        ll num2=0;
        for(int j=i-1;j>=1;j--)
        {
            num2=num2*10+num[j];
        }
        num2++;
        cnt[num[i]]+=num2;
        cnt[0]-=ten[i-1];
    }
}
int main()
{
    scanf("%lld%lld",&a,&b);
    ten[0]=1;
    for(int i=1;i<=15;i++)
    {
        f[i]=f[i-1]*10+ten[i-1];
        ten[i]=ten[i-1]*10;
    }
    query(a-1,cnta);
    query(b,cntb);
    for(int i=0;i<=9;i++)
     printf("%lld ",cntb[i]-cnta[i]);
    return 0;
}

数字计数1

递推处理:

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=15;
struct node{
    ll a[11];
    void init(){
        memset(a,0,sizeof a);
    }
    node operator +(const node &b){
        node c;c.init();
        for(int i=0;i<=9;i++)
           c.a[i]=a[i]+b.a[i];
        return c;
    }
    node operator *(const int &x){
        node c;c.init();
        for(int i=0;i<=9;i++)
         c.a[i]=a[i]*x;
        return c;
    }
    node operator -(const node &b){
        node c;c.init();
        for(int i=0;i<=9;i++)
           c.a[i]=a[i]-b.a[i];
        return c;
    }
    void op(){
        cout<<endl;
        for(int i=0;i<=9;i++)
         cout<<" "<<a[i];cout<<endl;cout<<endl;
    }
}f[N][2],ans,kk;
int num[N],cnt;
ll ten[N];
ll pre[N];
ll A,B;
void sol(){
    for(int i=0;i<=cnt+1;i++)
     for(int j=0;j<=1;j++)
      f[i][j].init();
    for(int i=cnt;i>=1;i--){
        //cout<<num[i]<<" dig"<<endl;
        f[i][1]=f[i+1][1];f[i][1].a[num[i]]++;
        f[i][0]=(f[i+1][1]*num[i])+(f[i+1][0]*10);
        for(int j=0;j<=9;j++)
        {
            if(j<num[i]) f[i][0].a[j]+=(pre[i+1]+1);
            else f[i][0].a[j]+=pre[i+1];
        }
        //f[i][1].op();
        //f[i][0].op();
    }
    for(int i=cnt;i>=1;i--){
        f[1][0].a[0]-=ten[i-1];
    }
}
int main()
{
    scanf("%lld%lld",&A,&B);ten[0]=1;
    for(int i=1;i<=13;i++)ten[i]=ten[i-1]*(ll)10;
    while(B){
        num[++cnt]=B%10;
        pre[cnt]=B;B/=10;
    }
    //pre[cnt+1]=1;
    sol();
    ans=ans+f[1][0]+f[1][1];

    cnt=0;memset(num,0,sizeof num);
    memset(pre,0,sizeof pre);
    A--;
    while(A){
        num[++cnt]=A%10;
        pre[cnt]=A;
        A/=10;
    }
    //pre[cnt+1]=1;

    sol();
    kk=kk+f[1][0]+f[1][1];
    ans=ans-kk;

    for(int i=0;i<=9;i++){
        printf("%lld ",ans.a[i]);
    }
    return 0;
}

数字计数2

[SCOI2009]windy数

windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,

在A和B之间,包括A和B,总共有多少个windy数?

Solution:

直接记录最后一个数是什么,要记录是否已经出现了非零数字,即前导零。处理前导零bug

因为前导零不处理,可能会认为最后一位填的是0,从而不能填1、0了。

f[i][last][0/1限制][0/1有无出现非零数字]

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=12;
int f[N][N][2][2];
int num[N],cnt;
int A,B;
int wrk(){
    memset(f,0,sizeof f);
    for(int i=1;i<num[cnt];i++) f[cnt][i][0][1]=1;
    f[cnt][num[cnt]][1][1]=1;
    f[cnt][0][0][0]=1;

    for(int i=cnt;i>=2;i--){
        for(int j=0;j<=9;j++){
            for(int k=0;k<=9;k++){
                if(abs(k-j)>=2){
                    if(j==num[i]&&k==num[i-1]) f[i-1][k][1][1]+=f[i][j][1][1];
                    if(j==num[i]&&k<num[i-1]) f[i-1][k][0][1]+=f[i][j][1][1];
                    f[i-1][k][0][1]+=f[i][j][0][1];
                    if(j==0) f[i-1][k][0][1]+=f[i][j][0][0];
                }
            }
        }
        f[i-1][0][0][0]+=f[i][0][0][0];
        f[i-1][1][0][1]+=f[i][0][0][0];
    }
    int ret=0;
    for(int j=0;j<=9;j++){
        ret+=f[1][j][0][1]+f[1][j][1][1];
    }
    //ret+=f[1][0][0][0];
    return ret;
}
int main()
{
    scanf("%d%d",&A,&B);
    while(B){
        num[++cnt]=B%10;B/=10;
    }
    int ansB=wrk();

    A--;cnt=0;
    while(A){
        num[++cnt]=A%10;A/=10;
    }
    int ansA=wrk();

    //cout<<ansB<<" "<<ansA<<endl;

    printf("%d",ansB-ansA);
    return 0;
}

windy数

SAC#1 - 萌数

辣鸡蒟蒻SOL是一个傻逼,他居然觉得数很萌!

好在在他眼里,并不是所有数都是萌的。只有满足“存在长度至少为2的回文子串”的数是萌的——也就是说,101是萌的,因为101本身就是一个回文数;110是萌的,因为包含回文子串11;但是102不是萌的,1201也不是萌的。

现在SOL想知道从l到r的所有整数中有多少个萌数。

由于答案可能很大,所以只需要输出答案对1000000007(10^9+7)的余数。

Solution:

抓住问题本质,长度至少为2的回文子串,只要有2或者3就一定满足。

所以,只要记录上一位,上上位填的数即可。

还要记录是否已经出现过长度为2或者3的回文串。

同样,前导零的锅也要处理的。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000+10;
const int mod=1e9+7;
int n,m;
char a[N],b[N];
int la,lb;
ll ans;
ll r,l;
ll f[N][2][2][12][12][2];// lim ; has huiwen ; has >=1
ll wrk(char *s,int len){
    memset(f,0,sizeof f);
    for(int j=0;j<=s[1]-‘0‘;j++){
        if(j!=0){
            if(j<s[1]-‘0‘)f[1][0][0][10][j][1]=1;
            else f[1][1][0][10][j][1]=1;
        }
        else f[1][0][0][10][10][0]=1;
    }
    if(len==1) return 0;
    for(int i=2;i<=lb;i++){
        for(int j=0;j<=10;j++){
            for(int k=0;k<=10;k++){
                for(int p=0;p<=9;p++){
                    if(p<s[i]-‘0‘){

                        if(p==k||p==j) (f[i][0][1][k][p][1]+=f[i-1][0][0][j][k][1]+f[i-1][1][0][j][k][1])%=mod;
                        else (f[i][0][0][k][p][1]+=f[i-1][0][0][j][k][1]+f[i-1][1][0][j][k][1])%=mod;
                        (f[i][0][1][k][p][1]+=f[i-1][0][1][j][k][1]+f[i-1][1][1][j][k][1])%=mod;
                    }
                    else if(p==s[i]-‘0‘){
                        if(p==k||p==j) (f[i][0][1][k][p][1]+=f[i-1][0][0][j][k][1])%=mod;
                        else (f[i][0][0][k][p][1]+=f[i-1][0][0][j][k][1])%=mod;
                        (f[i][0][1][k][p][1]+=f[i-1][0][1][j][k][1])%=mod;

                        if(p==k||p==j) (f[i][1][1][k][p][1]+=f[i-1][1][0][j][k][1])%=mod;
                        else (f[i][1][0][k][p][1]+=f[i-1][1][0][j][k][1])%=mod;
                        (f[i][1][1][k][p][1]+=f[i-1][1][1][j][k][1])%=mod;
                    }
                    else{
                        if(p==k||p==j) (f[i][0][1][k][p][1]+=f[i-1][0][0][j][k][1])%=mod;
                        else (f[i][0][0][k][p][1]+=f[i-1][0][0][j][k][1])%=mod;
                        (f[i][0][1][k][p][1]+=f[i-1][0][1][j][k][1])%=mod;
                    }
                }
            }
        }
        for(int p=1;p<=10;p++){
            if(p==10) (f[i][0][0][10][p][0]+=f[i-1][0][0][10][10][0])%=mod;
            else (f[i][0][0][10][p][1]+=f[i-1][0][0][10][10][0])%=mod;
        }
    }
    ll ret=0;
    for(int j=0;j<=9;j++)
     for(int k=0;k<=9;k++){
         (ret+=f[len][1][1][j][k][1]+f[len][0][1][j][k][1])%=mod;
     }
    return ret;
}
int main()
{
    scanf("%s",a+1);scanf("%s",b+1);
    la=strlen(a+1),lb=strlen(b+1);

    l=wrk(a,la);
    r=wrk(b,lb);

    bool fl=false;
    for(int i=2;i<=la;i++){
        if(a[i]==a[i-1]||a[i]==a[i-2]) fl=true;
    }
    ans=(r+fl-l+mod)%mod;
    printf("%lld",ans);
    return 0;
}

萌数

bzoj 3329 Xorequ

Description

Input

第一行一个正整数,表示数据组数据 ,接下来T行

每行一个正整数N

Output

2*T行

第2*i-1行表示第i个数据中问题一的解,

第2*i行表示第i个数据中问题二的解,

HINT

x=1与x=2都是原方程的根,注意第一个问题的解

不要mod 10^9+7

1<=N<=10^18

1<=T<=1000

Solution:

首先一定要化简x^3x=2x

即:3x=x^2x ; x+2x=x^2x 说明,2倍的x和x没有公共1位置,否则就会变小了。

2x就是x二进制下左移一位,所以,x的条件其实是,x的二进制表示下没有连续的两个1.

对于第一问,记录上一位填的是0/1即可转移。

对于第二问,发现,是2的整次幂。2^n显然满足条件,

这里假设可以是0,不需要正整数条件,而0显然也满足条件。

最后也不用减,因为2^n本来就要加1个。

即处理有n位可以填0/1。

设f[i]表示,i位随便填0/1,符合的数的个数。

第i位填1的话,i-1位只能填零,方案数就是f[i-2]

第i位填0的话,对i-1位没有影响,就是f[i-1]

所以f[i]=f[i-1]+f[i-2],f[1]=2,f[2]=3

一个斐波那契数列。

矩阵乘法优化一下即可。

Code:(多组数据记得memset)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=66;
const int mod=1e9+7;
ll f[N][2][2];
int num[N],cnt;
ll wrk1(){
    memset(f,0,sizeof f);
    f[cnt+1][1][0]=1;
    for(int i=cnt;i>=1;i--){
        for(int k=0;k<=1;k++){
            if(k<num[i]){
                if(k==1) f[i][0][k]+=f[i+1][0][0]+f[i+1][1][0];
                else f[i][0][k]+=f[i+1][0][0]+f[i+1][0][1]+f[i+1][1][0]+f[i+1][1][1];
            }
            else if(k==num[i]){
                if(k==1) f[i][1][k]+=f[i+1][1][0],f[i][0][k]+=f[i+1][0][0];
                else f[i][1][k]+=f[i+1][1][1]+f[i+1][1][0],f[i][0][k]+=f[i+1][0][0]+f[i+1][0][1];
            }
            else {
                if(k==1) f[i][0][k]+=f[i+1][0][0];
                else f[i][0][k]+=f[i+1][0][0]+f[i+1][0][1];
            }
        }
    }
    ll ret=0;
    ret=f[1][0][0]+f[1][0][1]+f[1][1][0]+f[1][1][1];
    return ret-1;
}
struct tr{
    ll a[3][3];
    void pre(){
        memset(a,0,sizeof a);
    }
    void init(){
        for(int i=1;i<=2;i++) a[i][i]=1;
    }
    tr operator *(const tr &b){
        tr c;c.pre();
        for(int i=1;i<=2;i++)
          for(int k=1;k<=2;k++)
            for(int j=1;j<=2;j++){
                (c.a[i][j]+=a[i][k]*b.a[k][j]%mod)%=mod;
            }
        return c;
    }
}A,S,B;
tr qm(tr x,ll y){
    tr ret;ret.pre();ret.init();
    while(y){
        if(y&1) ret=ret*x;
        x=x*x;
        y>>=1;
    }
    return ret;
}
int t;
ll n;
int main()
{
    scanf("%d",&t);
    A.a[1][1]=3,A.a[1][2]=2;
    B.a[1][1]=1,B.a[1][2]=1;
    B.a[2][1]=1,B.a[2][2]=0;

    while(t--){
        scanf("%lld",&n);
        ll nn=n;
        cnt=0;
        while(nn){
            num[++cnt]=nn%2;nn/=2;
        }
        ll ans1=wrk1();
        printf("%lld\n",ans1);

        ll ans2;
        if(n==1){
            ans2=2;
        }
        else if(n==2){
            ans2=3;
        }
        else{
            S=A*qm(B,n-2);
            ans2=S.a[1][1]%mod;
        }
        printf("%lld\n",ans2);
    }
    return 0;
}

Xorequ

CF55D Beautiful numbers

Volodya是一个很皮的男孩。他认为一个能被它自己的每一位数上的数整除(非0)的数是很妙的。我们先忽略他的想法的正确性,只回答在l到r之间有多少个很妙的数字。

输入输出格式

输入:总共有t个询问:

第一行:t;

接下来t行:每行两个数l和r。

注意:请勿使用%lld读写长整型(虽然我也不知道为什么),请优先使用cin(或者是%I64d)。

输出:t行,每行为一个询问的答案。

Solution:

比较巧妙的状态设计,还有优化 的题目。

直接记录被1,2,3..除是肯定不行的,。

发现,lcm(1,2.。。9)=2520

所以,如果最后能被1,2.。。9整除,那么把这个数对2520取模,不会影响整除与否的。

所以,f[i][2520][S]表示,填到i位,对2520取模结果,出现数字的状压集合。

但是,这个题有10组数据,就T飞了。

考虑优化没用的状态。

S大小是256的,不用记0,不用记1,要记8个。

但是,最后要找的还是S出现数字的lcm能否整除x

所以,就直接记录lcm就行了。

2250=2^3*3^2*5*7,一共只有48种lcm,离散化记录,256->48

但是还是过不去(可能我的代码常数太大?)

发现,记录mod 2250的余数时,假设之前是j,

那么,新的=j*10+k mod 2250(k是当前位的数)

设x=a2250+j;

每次j要乘10,是不是记录mod 225即可?

x=a‘225 + j‘

x‘=a‘2250+10j‘+k

发现,x mod 2250 的只和j‘有关,

所以,记录j‘即可,每次mod 225记录新的j

最后一次,mod 2250 就是mod 2250 的余数了。

Code:(luogu最优解)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=25;
const int up=252;
ll L,R;
int a[N],cnt;
ll f[N][252][2][50];
ll g[2525][2][50];
int lcm[1026],tot;
int id[2530];
int to[50][12];
int gcd(int p,int q){
    return q?gcd(q,p%q):p;
}
void dfs(int x,int lc){
    //cout<<" x lc "<<x<<" "<<lc<<endl;
    if(x==10){
        lcm[++tot]=lc;
        return;
    }
    dfs(x+1,lc);
    dfs(x+1,lc*x/gcd(x,lc));
}
ll wrk(){
    ll ret=0;
    memset(f,0,sizeof f);
    memset(g,0,sizeof g);
    f[cnt+1][0][1][1]=1;
    for(int i=cnt;i>=1;i--){
        for(int j=0;j<252;j++){
            for(int k=1;k<=tot;k++){
                for(int p=0;p<=9;p++){
                    if(i!=1){
                        if(p<a[i]){
                            f[i][(j*10+p)%up][0][to[k][p]]+=f[i+1][j][0][k]+f[i+1][j][1][k];

                        }
                        else if(p==a[i]){
                            f[i][(j*10+p)%up][0][to[k][p]]+=f[i+1][j][0][k];
                            f[i][(j*10+p)%up][1][to[k][p]]+=f[i+1][j][1][k];
                        }
                        else {
                            f[i][(j*10+p)%up][0][to[k][p]]+=f[i+1][j][0][k];
                        }
                    }
                    else{
                        if(p<a[i]){
                            g[(j*10+p)%2520][0][to[k][p]]+=f[i+1][j][0][k]+f[i+1][j][1][k];

                        }
                        else if(p==a[i]){
                            g[(j*10+p)%2520][0][to[k][p]]+=f[i+1][j][0][k];
                            g[(j*10+p)%2520][1][to[k][p]]+=f[i+1][j][1][k];
                        }
                        else {
                            g[(j*10+p)%2520][0][to[k][p]]+=f[i+1][j][0][k];
                        }
                    }
                }
            }
        }
    }
    for(int j=0;j<2520;j++){
        for(int k=1;k<=tot;k++){
            if(j%lcm[k]==0){
                ret+=g[j][1][k]+g[j][0][k];
            }
        }
    }

    return ret;
}
int T;
ll ansl,ansr;
int main()
{
    dfs(1,1);
    sort(lcm+1,lcm+tot+1);
    //cout<<tot<<endl;
    tot=unique(lcm+1,lcm+tot+1)-lcm-1;
    for(int i=1;i<=tot;i++) id[lcm[i]]=i;
    for(int i=1;i<=tot;i++){
        to[i][0]=i;
        for(int j=1;j<=9;j++){
            to[i][j]=id[lcm[i]*j/gcd(lcm[i],j)];
        }
    }

    /*cout<<" tot "<<tot<<endl;
    for(int i=1;i<=tot;i++){
        cout<<lcm[i]<<" "<<id[lcm[i]]<<" "<<endl;
        for(int j=0;j<=9;j++){
            cout<<" with "<<j<<" : "<<to[i][j]<<endl;
        }
    }*/

    scanf("%d",&T);
    while(T--){
        scanf("%I64d%I64d",&L,&R);
        ansl=ansr=0;
        L--;

        if(L==0) ansl=1;
        else{cnt=0;
            while(L){
                a[++cnt]=L%10;L/=10;
            }ansl=wrk();
        }

        cnt=0;
        while(R){
            a[++cnt]=R%10;R/=10;
        }ansr=wrk();

        printf("%I64d\n",ansr-ansl);
    }
    return 0;
}

Beautiful numbers

[SCOI2014]方伯伯的商场之旅

非常麻烦的数位dp了。

见另外一篇博客:

[SCOI2014]方伯伯的商场之旅

原文地址:https://www.cnblogs.com/Miracevin/p/9534844.html

时间: 2024-10-10 15:41:43

数位dp——奏响数字数位的美妙乐章的相关文章

UESTC 250 数位dp(数字相位数之间的差值不小于2)

http://acm.uestc.edu.cn/#/problem/show/250 windy定义了一种windy数. 不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? Input 包含两个整数,A B. 满足 1≤A≤B≤2000000000 . Output Sample input and output Sample Input Sample Output 1 10 9 Source Windy /*

hdu(2089)——不要62(数位dp)

这道题很早之前就用数位dp做过一遍,但是那时候并没有理解透彻.今天又细细思考了一下,感觉比之前要好多了 题意: 就是叫你在[n,m]这个区间范围内找出不包含4和62的数字的个数. 思路: 1)暴力 2)数位dp: 做数位dp前,我们首先需要初始化,我们定义f[i][j]为开头为j的i位数中正常的数字有几个. 然后我们可以用三层for循环来对f数组进行初始化. void init(){ f[0][0]=1;<span style="white-space:pre"> <

HDU 3555 Bomb ,HDU 2089 深刻学习数位dp (各种方法乱用)

数位dp是与数位有关的区间统计 参考: 点击打开链接 思想是将数字拆分,dp[i][j] 长度为i的数字有多少个满足j条件的,从000~999 统计时,计当前数字为x,则其后面数字的变化的倍数是0~x-1 后面接000~999这样刚好统计不到这个数字n本身. eg: 对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制.此时如果能直接处理这一情况,则问题距离解决又会迈出一大步. 例如,在十进制下,计算[10000,54321]内的数字和,我们可以将其分解为: [10000,19999],[

数位dp对于状态描述与发现的一些感悟

今天刷的数位dp 第一题看了题解以后知道了数位dp的基本板子,写数位dp的方式(运用记忆化递归的方法)已经基本固定. 那么接下来的难点主要还是对于题目描述的问题,如何抽象成dp中的状态.就今天刷的题来看,dp数组第一维一般为第i位数,这是数位dp的一般表示数的方式.而数组究竟还要加几维就要看题目要求的东西. 如下题: http://acm.hdu.edu.cn/showproblem.php?pid=3709 题意即求: 对于某个 number,你可以 fix a pivot 在某位,然后如果分

【一天一DP计划】数位dp

入坑之好博 一本通 数位DP 浅谈数位DP ## P4127 同类分布 现在关键的问题是:怎样记录dp状态? 这里 st可达到 1e18 显然是不能作为dp转移的下标直接记录的 所以我们考虑取模 我们最理想的模数当然是把每次搜到最后得到的数字各个位数之和 但是我们发现在这个过程中 sum是发生变化的 所以我们就应该以一个定值作为模数 那好,我们虽然不知道最后各位之和的结果,我们枚举总可以吧 我们只需要枚举所有的各位数字之和作为模数 最后判断 sum 和枚举的 mod相等并且 st%sum=0 的

11 .3 数位dp

数位dp是以数位上的关系为转移关系而进行的一种计数dp,题目基本类型是给定区间[l ,r] ,求l到r之间满足要求的数字的个数 . dp状态的转移方式通常是用 递归+记忆化搜索 ,转移顺序一般是由高数位转移到底数位 ,其中就是记忆化搜索保证了数位dp的高效率 例如千位2到百位转移要枚举0,1,2,3 ...(2000,2100,2200,2300...) ,而千位3也是同样的(3000,3100,3200,3300...),其进行的都是对三位数000~999的统计,所以低位统计过程只用进行一次就

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

BZOJ_1833_[ZJOI2010]_数字计数_(数位dp)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1833 统计\(a~b\)中数字\(0,1,2,...,9\)分别出现了多少次. 分析 数位dp真是细节又多又容易出错,我都懒得看题解,所以也就懒得写题解了... 注意细节吧还是... 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 ll a,b; 6 ll A[10],B[10],n

hdu3709 数位dp(自身平衡的数字)

http://acm.hdu.edu.cn/showproblem.php?pid=3709 Problem Description A balanced number is a non-negative integer that can be balanced if a pivot is placed at some digit. More specifically, imagine each digit as a box with weight indicated by the digit.