算法模板——数位dp

来源:互联网 发布:上海1943和老街知乎 编辑:程序博客网 时间:2024/06/03 22:48

数位dp
顾名思义,数位dp就是对每个数位进行dp。一般来说,数位dp的适用范围是求一个区间[a,b]的值,其中a和b会很大,而且值具有可加性。那么对于每个值可以分成一些段,如:2333可以分成求0-999,1000-1999,2000-2299,2300-2329,2330-2333这些段的值。

代码

//[ZJOI2010]count数字计数#include <cstdio>#include <cmath>const int maxn=12;struct data{    long long num[10];    data operator +(const data &other)    {        data res;        for(int i=0; i<=9; i++)        {            res.num[i]=num[i]+other.num[i];        }        return res;    }};data f[maxn+1][10];long long t[maxn+1],a,b;int init();data calc(long long n);int main(){    init();    scanf("%lld%lld",&a,&b);    data x=calc(a-1),y=calc(b);    for(int i=0; i<9; i++)    {        printf("%lld ",y.num[i]-x.num[i]);    }    printf("%lld\n",y.num[9]-x.num[9]);    return 0;}int init(){    t[1]=1;    for(int i=2; i<=13; i++)    {        t[i]=t[i-1]*10ll;    }    for(int i=0; i<=9; i++)    {        f[1][i].num[i]=1;    }    for(int i=2; i<=12; i++)    {        for(int j=0; j<=9; j++)        {            for(int k=0; k<=9; k++)            {                f[i][k]=f[i][k]+f[i-1][j];                f[i][k].num[k]+=t[i-1];            }        }    }    return 0;}data calc(long long n){    data ans;    ans.num[0]=1;    for(int i=1; i<=9; i++)    {        ans.num[i]=0;    }    if(!n)    {        return ans;    }    int len=log(n)/log(10)+1;    for(int i=1; i<len; i++)    {        for(int j=1; j<=9; j++)        {            ans=ans+f[i][j];        }    }    int h=n/t[len];    n%=t[len];    for(int i=1; i<h; i++)    {        ans=ans+f[len][i];    }    ans.num[h]+=n+1;    for(int i=len-1; i>0; i--)    {        h=n/t[i];        n%=t[i];        for(int j=0; j<h; j++)        {            ans=ans+f[i][j];        }        ans.num[h]+=n+1;    }    return ans;}