CodeForces 55D Beautiful numbers(数位dp)

时间:2021-08-30 07:26:53

数位dp,三个状态,dp[i][j][k],i状态表示位数,j状态表示各个位上数的最小公倍数,k状态表示余数

其中j共有48种状态,最大的是2520,所以状态k最多有2520个状态。

#include<stdio.h>
#include<math.h>
#include<string.h>
#define LL long long
LL dp[20][50][2520];
LL div[50],rdiv[2600],po[30];
LL a[4]={2,3,5,7},num[4]={3,2,1,1},cou=0,p[30];
void getdiv(int n,LL cur)//获取48个j状态
{
if(n==4){
div[cou]=cur;
rdiv[cur]=cou++;
return ;
}
getdiv(n+1,cur);
for(int i=0;i<num[n];i++)
{
cur*=a[n];
getdiv(n+1,cur);
}
}
void getpo()
{
po[1]=1;
for(int i=2;i<20;i++)
po[i]=po[i-1]*10;
}
LL gcd(LL a,LL b)
{
if(b==0)return a;
return gcd(b,a%b);
}
LL lcm(LL a,LL b)
{
if(b==0)return a;
LL p=gcd(a,b);
return a/p*b;
}
void init()//预处理出dp[i][j][k]
{
getdiv(0,1);
getpo();
int i,j,k,l;
LL tm,ss;
dp[0][0][0]=1;
for(i=1;i<20;i++){
for(j=0;j<10;j++){
for(k=0;k<48;k++){
for(l=0;l<2520;l++){
tm=(l+j*po[i])%2520;
ss=rdiv[lcm(div[k],j)];
dp[i][ss][tm]+=dp[i-1][k][l];
}
}
}
}
}
LL solve(LL n)
{
int i,j,k,l;
for(i=1;n;i++)
{
p[i]=n%10;
n/=10;
}
int len=i;
LL ans=0,cur1=0,cur2=1,tm,ss;//cur1表示前几位上的数的余数,cur2表示前几位的数的最小公倍数
for(i=len-1;i>0;i--)
{
for(j=0;j<p[i];j++)
{
for(k=0;k<48;k++)
{
ss=lcm(cur2,j);
ss=lcm(ss,div[k]);
tm=(cur1+j)*po[i]%ss;
for(l=(ss-tm)%ss;l<2520;l+=ss)//处理48种状态,2520个余数上合法的数
ans+=dp[i-1][k][l];
}
}
cur2=lcm(cur2,p[i]);
cur1=(cur1+p[i])*10%2520;
}
return ans;
}
int main()
{
int i,j,k,t;
LL m,n;
init();
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d",&m,&n);
printf("%I64d\n",solve(n+1)-solve(m));
}
return 0;
}