zoukankan      html  css  js  c++  java
  • CodeForces 55D "Beautiful numbers"(数位DP+离散化处理)

    传送门

    参考资料:

      [1]:CodeForces 55D Beautiful numbers(数位dp&&离散化)

    题意:

       求一个区间内的Beautiful numbers有多少个。Beautiful numbers指:一个数能整除所有组成它的非0数字。 
      例如15可以被1和5整除,所以15是Beautiful numbers。

    我的理解:

      起初,我先定义一个三维数组 dp[ i ][ j ][ k ]:来到 i 位置时,所有非零数的lcm = j,当前数位 k 时含有的 Beautiful numbers 的个数。

      但是,由题意得,当前的数 k 可以是个很大的数(9e18),数组根本就开不下,那怎么办呢?

      将当前的数 hash 一下,如何hash呢?

      假设 a,b,c,d 为[0,9]的数,那么不存在另一个 a',b',c',d'

      使得 a*11+b*13+c*17+d*19 = a' *11+b' *13+c' *17+d' *19;

      也就是说,这 19 位数分别乘以大于10 的互不相等的前19个素数是不会存在重数的;

      那么,hash完后,最大的数才7000多,那么开个 dp[20][50][8000] 的数组绰绰有余;

      但问题来了,既然每个数的 hash 值都不想等,那,哪来的记忆化搜索?

      所以说,这就是赤裸裸的暴力!!!!!!

      so,举足无措,只好参考大佬博客辽。

      下面具体说说我的理解:

      首先解释一下上述博客中给出的公式 sum%(x*n)%x == sum%x 的证法以及作用;

      证明:

        sum%(x*n) = sum-(int)(sum / (x*n) )*(x*n);

        sum%(x*n)%x =  [sum-(int)(sum / (x*n) )*(x*n) ] % x = sum%x - 0 = sum % x;

      那,接下来就要看看要怎么用这个公式了,预处理出 1~9的所有不同组合的 lcm ,我是用DFS预处理的;

    参考代码如下:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 #define mem(a,b) memset(a,b,sizeof(a))
     7 
     8 int a[48];
     9 bool vis[10];//vis[i]:判断数字 i 是否访问过
    10 bool vis2[2*3*4*5*6*7*8*9];//vis2[lcm]:判断当前的lcm是否在之前出现过
    11 
    12 int GCD(int a,int b)
    13 {
    14     return a==0 ? b:GCD(b%a,a);
    15 }
    16 int LCM(int a,int b)
    17 {
    18     return a*b/GCD(a,b);
    19 }
    20 
    21 void DFS(int t,int lcm,int &k)
    22 {
    23     if(!vis2[lcm])
    24     {
    25         a[k++]=lcm;
    26         vis2[lcm]=true;
    27     }
    28 
    29     for(int i=1;i <= 9;++i)
    30     {
    31         if(vis[i])
    32             continue;
    33         vis[i]=true;
    34         DFS(t+1,LCM(lcm,i),k);
    35         vis[i]=false;
    36     }
    37 }
    38 int main()
    39 {
    40     int k=0;
    41 
    42     mem(vis,false);
    43     mem(vis2,false);
    44 
    45     DFS(1,1,k);
    46     sort(a,a+48);
    47     for(int i=0;i < k;++i)
    48         printf("%d,",a[i]);
    49 
    50     return 0;
    51 }
    预处理出所有的lcm

      与处理完后,你会发现最大的lcm为2520,且 2520%(其余的lcm) = 0;

      那么,这就好办了,对于当前所形成的数 curNum 和当前的 lcm,我们需要判断 curNum % lcm 是否等于0,那么问题就是

      curNum 太大了怎么办?

      通过上面的公式,可知 curNum%lcm = curNum%(lcm*(2520/lcm) )%lcm = (curNum%2520)%lcm;

      所以,每次只需要将 curNum%2520 即可。  

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 #define ll long long
     7 #define mem(a,b) memset(a,b,sizeof(a))
     8 
     9 ll n,m;
    10 int digit[20];
    11 ll dp[20][50][2520];
    12 int a[48]={1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10,
    13            12,14,15,18,20,21,24,28,30,35,
    14            36,40,42,45,56,60,63,70,72,84,
    15            90,105,120,126,140,168,180,210,
    16            252,280,315,360,420,504,630,840,1260,2520};
    17 
    18 int GCD(int a,int b)
    19 {
    20     return a==0 ? b:GCD(b%a,a);
    21 }
    22 int LCM(int a,int b)
    23 {
    24     return a*b/GCD(a,b);
    25 }
    26 
    27 ll DFS(int curPos,int curNum,int lcm,bool limit)
    28 {
    29     if(curPos == -1)
    30         return curNum%lcm == 0 ? 1:0;
    31     int t=lower_bound(a,a+48,lcm)-a;
    32     if(!limit && dp[curPos][t][curNum] != -1)
    33         return dp[curPos][t][curNum];
    34 
    35     int up=limit ? digit[curPos]:9;
    36     ll ans=0;
    37     for(int i=0;i <= up;++i)
    38         ans += DFS(curPos-1,(curNum*10+i)%2520,(i != 0 ? LCM(lcm,i):lcm),limit&&i==digit[curPos]);
    39     if(!limit)
    40         dp[curPos][t][curNum]=ans;
    41 
    42     return ans;
    43 }
    44 ll Solve(ll x)
    45 {
    46     int k=0;
    47     while(x)
    48     {
    49         digit[k++]=x%10;
    50         x /= 10;
    51     }
    52     return DFS(k-1,0,1,true);
    53 }
    54 int main()
    55 {
    56     int test;
    57     scanf("%d",&test);
    58     mem(dp,-1);
    59     while(test--)
    60     {
    61         scanf("%lld%lld",&n,&m);
    62         printf("%lld
    ",Solve(m)-Solve(n-1));
    63     }
    64     return 0;
    65 }
    View Code
  • 相关阅读:
    MacOS的多重启动工具
    exchange 2003配置ASSP 反垃圾邮件
    VMWARE workstation 9 收缩虚拟硬盘
    [AX2012]在SSRS报表中获取从Menuitem传入的记录
    [AX2012]代码更改默认财务维度
    js使用模板快速填充数据
    CI框架--事务
    $().each 和表单事件的坑
    基于layerpage 前后端异步分页
    SQL 语句格式
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10598326.html
Copyright © 2011-2022 走看看