[BZOJ3679][数位DP]数字之积

7 篇文章 0 订阅

学一发数位DP
如果直接记录乘积的话有 109 的情况,但是因为每一位只有1~9,所以最后乘积只有2 3 5 7的质因数,那么把在第i为放0~9转化成放入多少个2 3 5 7
f(i,c2,c3,c5,c7,j) 表示转移到从高到低的第i位,因数2的个数为c2,3的个数为c3,5的个数为c5,7的个数为c7,j表示到i位的数字是否等于R的,然后转移一下 就好了…

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;

int n;
int w[20],wt;
int add[10][5];
ll f[2][32][21][14][13][2];
ll p2[32],p3[21],p5[14],p7[13];
ll L,R;

inline ll solve(ll lt,int n){
  memset(f,0,sizeof(f));
  int k=0; wt=0; ll x=lt,ret=0;
  while(x) w[++wt]=x%10,x/=10;
  for(int i=1;i<=w[wt];i++)
    if(i>n) break; else
      if(i==w[wt]) f[0][add[i][1]][add[i][2]][add[i][3]][add[i][4]][1]=1;
      else f[0][add[i][1]][add[i][2]][add[i][3]][add[i][4]][0]=1;
  for(int j=wt-1;j;j--){
    k^=1;
    for(int i=1;i<=9;i++)
      if(i<=n) f[k][add[i][1]][add[i][2]][add[i][3]][add[i][4]][0]=1;
    for(int i1=0;i1<=30;i1++)
      if(p2[i1]<=n)
    for(int i2=0;i2<=19;i2++)
      if(p3[i2]*p2[i1]<=n)
        for(int i3=0;i3<=13;i3++)
          if(p5[i3]*p3[i2]*p2[i1]<=n)
        for(int i4=0;i4<=11;i4++)
          if(p7[i4]*p2[i1]*p3[i2]*p5[i3]<=n){
            ll now=p2[i1]*p3[i2]*p5[i3]*p7[i4];
            for(int i=1;i<=9;i++){
              if(i*now>n) break;
              f[k][i1+add[i][1]][i2+add[i][2]][i3+add[i][3]][i4+add[i][4]][0]+=f[k^1][i1][i2][i3][i4][0];
              if(i>w[j]) continue;
              if(i<w[j]) f[k][i1+add[i][1]][i2+add[i][2]][i3+add[i][3]][i4+add[i][4]][0]+=f[k^1][i1][i2][i3][i4][1];
              else f[k][i1+add[i][1]][i2+add[i][2]][i3+add[i][3]][i4+add[i][4]][1]+=f[k^1][i1][i2][i3][i4][1];
            }
          }
    for(int i1=0;i1<=30;i1++)
      for(int i2=0;i2<=19;i2++)
    for(int i3=0;i3<=13;i3++)
      for(int i4=0;i4<=11;i4++)
        f[k^1][i1][i2][i3][i4][0]=f[k^1][i1][i2][i3][i4][1]=0;
  }
  for(int i1=0;i1<=30;i1++)
    for(int i2=0;i2<=19;i2++)
      for(int i3=0;i3<=13;i3++)
    for(int i4=0;i4<=11;i4++)
      ret+=f[k][i1][i2][i3][i4][0],f[k][i1][i2][i3][i4][0]=f[k][i1][i2][i3][i4][1]=0;
  return ret;
}

int main(){
  cin>>n>>L>>R;
  p2[0]=p3[0]=p5[0]=p7[0]=1;
  for(int i=1;i<=30;i++) p2[i]=p2[i-1]*2;
  for(int i=1;i<=19;i++) p3[i]=p3[i-1]*3;
  for(int i=1;i<=13;i++) p5[i]=p5[i-1]*5;
  for(int i=1;i<=11;i++) p7[i]=p7[i-1]*7;
  add[2][1]=1; add[3][2]=1; add[4][1]=2; add[5][3]=1;
  add[6][1]=1; add[6][2]=1; add[7][4]=1; add[8][1]=3;
  add[9][2]=2;
  cout<<solve(R,n)-solve(L,n)<<endl;
  return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值