CodeForces 55D Beautiful numbers(数位dp)

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

}


 

 

你可能感兴趣的:(codeforces)