BZOJ 3679 数字之积 - 数位dp

题目描述

分析(From Claris):

考虑计算[1;R)内满足条件的数的个数。
数字之积非常大,但是这些数字的质因子只可能是2、3、5、7。
所以设f(i;cnt2;cnt3;cnt5;cnt7;j)为从高到低填了前i 位,2、3、5、7的个数分别为cnt2、cnt3、cnt5、cnt7,是否小于R的状态为j 的数字个数,然后DP即可。

#include<cstdio>
#include<cstring>
#define MAXTW 54
#define MAXTH 54
#define MAXFV 18
#define MAXSV 18
#define MAXST 1092025
typedef long long LL;

struct node{
    int tw,th,fv,sv;
    void Insert(int a,int b,int c,int d){
        tw=a,th=b,fv=c,sv=d;
    }
}st[2][MAXST+10][2];

int n,prime[20]={0,2,3,5,7},fac[20][10],cnt[2][2],lmt[20];
int id[2][MAXTW+1][MAXTH+1][MAXFV+1][MAXSV+1][2];
LL dp[2][MAXST+10][2];

int Getnum(LL x){
    memset(lmt,0,sizeof lmt);
    int ret=0,k=0;
    for(LL y=x;y;y/=10)
        ret++;
    for(LL y=x;y;y/=10,k++)
        lmt[ret-k]=y%10;
    return ret;
}
void Insert(int f,int tw,int th,int fv,int sv,int cmp,LL val)
{
    if(id[f][tw][th][fv][sv][cmp]==-1){
        id[f][tw][th][fv][sv][cmp]=++cnt[f][cmp];
        st[f][cnt[f][cmp]][cmp].Insert(tw,th,fv,sv);
        dp[f][cnt[f][cmp]][cmp]=val;
    }
    else
        dp[f][id[f][tw][th][fv][sv][cmp]][cmp]+=val;
}
bool check(node x,int a1,int a2,int a3,int a4)
{
    LL k=1;
    for(int i=x.tw+a1;i>=1;i--)
        k*=2;
    for(int i=x.th+a2;i>=1;i--)
        k*=3;
    for(int i=x.fv+a3;i>=1;i--)
        k*=5;
    for(int i=x.sv+a4;i>=1;i--)
        k*=7;
    if(k>0&&k<=n) return true;
    else return false;
}
void Getclear(int f)
{
    cnt[f][0]=cnt[f][1]=0;
    memset(dp[f],0,sizeof dp[f]);
    memset(st[f],0,sizeof st[f]);
    memset(id[f],-1,sizeof id[f]);
}
LL DP(LL num)
{
    if(!num) return 0;
    int m=Getnum(num),f=0;
    Getclear(f);
    for(int i=1;i<lmt[1];i++)
        Insert(f,fac[i][1],fac[i][2],fac[i][3],fac[i][4],0,1);
    Insert(f,fac[lmt[1]][1],fac[lmt[1]][2],fac[lmt[1]][3],fac[lmt[1]][4],1,1);
    for(int i=2;i<=m;i++){
        Getclear(f^1);
        Insert(f,0,0,0,0,0,1);
        for(int j=1;j<=cnt[f][0];j++){
            for(int k=1;k<=9;k++){
                if(!check(st[f][j][0],fac[k][1],fac[k][2],fac[k][3],fac[k][4]))
                    continue;
                Insert(f^1,st[f][j][0].tw+fac[k][1],st[f][j][0].th+fac[k][2],st[f][j][0].fv+fac[k][3],st[f][j][0].sv+fac[k][4],0,dp[f][j][0]);
            }
        }
        for(int j=1;j<=cnt[f][1];j++){
            for(int k=1;k<lmt[i];k++){
                if(!check(st[f][j][1],fac[k][1],fac[k][2],fac[k][3],fac[k][4]))
                    continue;
                Insert(f^1,st[f][j][1].tw+fac[k][1],st[f][j][1].th+fac[k][2],st[f][j][1].fv+fac[k][3],st[f][j][1].sv+fac[k][4],0,dp[f][j][1]);
            }
            if(lmt[i]==0) continue;
            if(!check(st[f][j][1],fac[lmt[i]][1],fac[lmt[i]][2],fac[lmt[i]][3],fac[lmt[i]][4]))
                continue;
            Insert(f^1,st[f][j][1].tw+fac[lmt[i]][1],st[f][j][1].th+fac[lmt[i]][2],st[f][j][1].fv+fac[lmt[i]][3],st[f][j][1].sv+fac[lmt[i]][4],1,dp[f][j][1]);
        }
        f^=1;
    }
    LL ret=0;
    for(int d=0;d<2;d++)
        for(int j=1;j<=cnt[f][d];j++)
            ret+=dp[f][j][d];
    return ret;
}
void prepare()
{
    for(int i=1;i<=9;i++){
        for(int j=1,x=i;j<=4;j++){
            if(x%prime[j]) continue;
            fac[i][0]++;
            while(x%prime[j]==0){
                x/=prime[j];
                fac[i][j]++;
            }
        }
    }
}
int main()
{
    LL L,R;
    prepare();
    scanf("%d%lld%lld",&n,&L,&R);
    printf("%lld\n",DP(R-1)-DP(L-1));
    return 0;
}
posted @ 2016-02-23 20:23  KatarinaYuan  阅读(195)  评论(0编辑  收藏  举报