• 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
    ",solve(n+1)-solve(m));
    	}
    	return 0;
    }
    


  • 相关阅读:
    回顾
    单例模式
    元类
    反射和内置方法
    issubclass 和 isinstance和断点调试
    绑定方法和非绑定方法
    并发编程:IO多路复用。
    基于tcp的下载文件,以及struct模块的应用。
    并发编程:协程,异步调用。
    并发编程:GIL,线程池,进程池,阻塞,非阻塞,同步,异步
  • 原文地址:https://www.cnblogs.com/pangblog/p/3260427.html
Copyright © 2020-2023  润新知