0x5C 数位统计DP

怎么说,数位DP还是我的噩梦啊,细节太恐怖了。

但是这章感觉又和之前的学的数位DP有差异?(应该是用DP预处理降低时间复杂度,好劲啊,不过以前都是记忆化搜索的应该不会差多少)

poj3208 f[i][0~2]表示第i位,开头连续j个6的情况数,[3]表示魔鬼数的个数,这样可以方便得出区间内有多少魔鬼数,不停的试填到底即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;

LL f[20][4];
void initf()
{
    f[0][0]=1;
    f[1][0]=9,f[1][1]=1,f[1][2]=f[1][3]=0;
    for(int i=2;i<=18;i++)
    {
        f[i][0]=9*(f[i-1][0]+f[i-1][1]+f[i-1][2]);
        f[i][1]=f[i-1][0];
        f[i][2]=f[i-1][1];
        f[i][3]=f[i-1][2]+10*f[i-1][3];
    }
}
int main()
{
    initf();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        int w=1;while(f[w][3]<n)w++;
        
        int h6=0;
        for(int i=w;i>=1;i--)
        {
            for(int j=0;j<=9;j++)
            {
                LL cc=f[i-1][3];
                if(j==6||h6==3)
                    for(int k=max(3-h6-(j==6),0);k<=2;k++)cc+=f[i-1][k];
                    
                if(cc<n)n-=cc;
                else
                {
                    if(h6<3)
                    {
                        if(j==6)h6++;
                        else h6=0;
                    }
                    printf("%d",j);
                    break;
                }
            }
        }
        printf("
");
    }
    return 0;
}
poj3208

月之谜 这题找不到原题,但是lyd有数据。一个看似很复杂多余实则很重要的定义:f[i][j][k][p]表示位数为i,数字和为j,对k取模等于p的数的个数,k这一维无法省略,原因是当前面增加一个数字j改变对应的模数也改变。预处理完了,就一位一位枚举,只有上限边缘是无法确定值的,上限之下都可以通过预处理求得。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
int MOD(int d,int mod){return (d%mod+mod)%mod;}

int mi[20],f[13][110][110][110];
void initf()
{
    mi[1]=1;for(int i=2;i<=10;i++)mi[i]=mi[i-1]*10;
    
    memset(f,0,sizeof(f));
    for(int j=0;j<=9;j++)
        for(int k=1;k<=100;k++)
            f[1][j][k][j%k]++;
    for(int i=2,mi=10;i<=10;i++,mi*=10)
    {
        for(int m=0;m<=9;m++)
            for(int j=m;j<=100;j++)
                for(int k=1;k<=100;k++)
                    for(int p=0;p<100;p++)
                        f[i][j][k][p]+=f[i-1][j-m][k][MOD(p-mi*m,k)];
    }
}

int maxw(int i)
{
    int ret=0;
    while(i!=0)i/=10,ret++;
    return ret;
}
int getnum(int x,int i){return x/(mi[i])%10;}
bool calc(int x)
{
    int k=x,sum=0;
    while(k!=0)sum+=k%10,k/=10;
    if(sum==0)return 0;
    return x%sum==0;
}

int getmoon(int x,int sum,int pun,int i)
{
    if(i==1)
    {
        int num=getnum(x,1),ret=0;
        for(int i=0;i<=num;i++)
        {
            x=x-x%10+i;
            if(calc(x)==true)ret++;
        }
        return ret;
    }
    
    int num=getnum(x,i),ret=0;
    for(int m=0;m<num;m++)
    {
        for(int j=max(1,sum+m);j<=100;j++)
            ret+=f[i-1][j-(sum+m)][j][MOD(-pun-mi[i]*m,j)];
    }
    ret+=getmoon(x,sum+num,pun+mi[i]*num,i-1);
    return ret;
}

int main()
{
    freopen("mystery.in","r",stdin);
    freopen("mystery.out","w",stdout);
    initf();
    int L,R;
    while(scanf("%d%d",&L,&R)!=EOF)
    {
        if(L==1)printf("%d
",getmoon(R,0,0,maxw(R)));
        else printf("%d
",getmoon(R,0,0,maxw(R))-getmoon(L-1,0,0,maxw(L-1)));
    }
    return 0;
}
月之谜
原文地址:https://www.cnblogs.com/AKCqhzdy/p/9484234.html