数位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; }