【题解】[牛客网NOIP赛前集训营-提高组(第一场)]B.数数字 线性DP
#include<cstdio>
#include<cstring>
typedef long long ll;
#define _rep(i,a,b) for(int i=(a);i<=(b);i++)
#define _for(i,a,b) for(int i=(a);i<(b);i++)
int co[10][10];
ll pw[10][100],l,r,l1,r1,ans,g[2][3][2][2],f[2][3][60][38][27][22];
ll nozero(ll x)
{
if(x<=0)return 0;
memset(f,0,sizeof(f));
int cr=0;f[0][1][0][0][0][0]=1;
ll ret=0,val;
for(;x;x/=10,cr^=1)
{
int bit=x%10;
memset(f[cr^1],0,sizeof(f[cr]));
_for(i,0,3)_for(n2,0,60)_for(n3,0,38)_for(n5,0,27)_for(n7,0,22)if(val=f[cr][i][n2][n3][n5][n7])
{
ll q1=r1/pw[2][n2]/pw[3][n3]/pw[5][n5]/pw[7][n7];
ll p1=(l1-1)/pw[2][n2]/pw[3][n3]/pw[5][n5]/pw[7][n7];
if(q1>=1&&p1<=0)ret+=val;
if(q1<=0)continue;
_for(p,1,10)
{
int ni=(p>bit?2:(p==bit?i:0));
int c2=n2+co[p][2];
int c3=n3+co[p][3];
int c5=n5+co[p][5];
int c7=n7+co[p][7];
if(c2>=60||c3>=38||c5>=27||c7>=22)continue;
f[cr^1][ni][c2][c3][c5][c7]+=val;
}
}
}
_for(i,0,2)_for(n2,0,60)_for(n3,0,38)_for(n5,0,27)_for(n7,0,22)if(val=f[cr][i][n2][n3][n5][n7])
{
ll q1=r1/pw[2][n2]/pw[3][n3]/pw[5][n5]/pw[7][n7];
ll p1=(l1-1)/pw[2][n2]/pw[3][n3]/pw[5][n5]/pw[7][n7];
if(q1>=1&&p1<=0)ret+=val;
}
return ret;
}
ll zero(ll x)
{
if(l1)return 0;//如果含0乘积为0
memset(g,0,sizeof(g));
int cr=0;ll ret=0;
g[0][1][0][0]=1;
for(;x;x/=10,cr^=1)
{
int bit=x%10;//取出该位
memset(g[cr^1],0,sizeof(g[cr]));
_for(i,0,3)_for(j,0,2)_for(hd,0,2)if(g[cr][i][j][hd])
{
if(hd&&j)ret+=g[cr][i][j][hd];
_for(p,0,10)
{
int ni=(p>bit?2:(p==bit?i:0));
int nj=(j|(p==0));
int hdt=(p>0);
g[cr^1][ni][nj][hdt]+=g[cr][i][j][hd];
}
}
}
return ret+g[cr][0][1][1]+g[cr][1][1][1];
}
ll calc(ll x)
{
return zero(x)+nozero(x);
}
int main()
{
//freopen("in.txt","r",stdin);
_for(i,1,10)
{
_for(j,2,10)
for(int k=i;k%j==0;k/=j)
co[i][j]++;//预处理出i有多少个j因子
pw[i][0]=1;
for(int j=1;j<=60;j++)
pw[i][j]=pw[i][j-1]*i;//预处理出i的次幂
}
scanf("%lld%lld%lld%lld",&l,&r,&l1,&r1);
if(!l)ans+=(l1==0),l++;
if(l<=r)ans+=calc(r)-calc(l-1);
printf("%lld\n",ans);
return 0;
}
总结
线性DP好题,状态定义是关键