DP·数位DP(2)

来源:互联网 发布:承接淘宝服装代加工 编辑:程序博客网 时间:2024/06/07 20:24

题目:

CodeForces 55D

 

题意:

若一个数能被其各个位上的数整除,即满足条件。求区间[l, r]内所有满足条件的数

 

方法:

若想要一个数能被其各个数位上的数整除,则此数要能被各个位上的数的LCM整除。而且由于LCM(1~9) = 2520,所以将数对2520取模后,若余数能被LCM整除,则原数也能被LCM整除,因为LCM肯定为2520的一个因子。

(期初几个数位DP都是用循环写的,结果后来发现有dfs的更具普遍应用的写法,更容易理解且能被更广地使用,虽然速度肯定不如循环的写法……但胜在简单实用)

数位DP,通过[l, r] = [1,r]- [1,l-1]求得最终答案(其实此方法也可以看成一种记忆化搜索,只是通过数位DP的思想优化了状态,减少了大量的复杂度)

 

定义状态:dfs(int n, int lcm, int m, bool flag)表示当前为第n位,之前所有位的LCM为lcm,且对之前构造出来的数对2520取模后为m,flag表示之前枚举的数知否全部到达上限(对于4567,则4512的前两位到达上限),即当前枚举的数是否受限

初始状态:dfs(len,1, 0, true)。

状态转移:

1若n为0,则枚举完了整个数列,返回m% lcm == 0。

2若之前所有数都达到了上限,即flag为true,则此位置可枚举的数为0~num[n](表示原数的第n位)

3若之前的数中有一个没到上限,则此位置无论是什么数,都不可能比原数大(类似循环的数位DP那个思想),则此位置可枚举的数为0~9

优化:由于1~9的数组合成的LCM是离散的,所以通过离散化lcm可减少空间复杂度。通过一个f[n][lcm][m]的数组进行记忆存储,减少时间复杂的,此数组存的是当长度为n,LCM为lcm,余数为m时所有满足条件的数的个数,所以要直接返回f数组的值时,不仅需要f数组的值存在,还要flag为false,即此时不受限

PS:由于第一次f数组开成了f[n][m][lcm]……结果调试了半天……算是自己把自己又坑了一回


 

测试数据:

2

1 1000

1 10000

 

答案:

138

752

 

 

 

#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<string>#include<algorithm>#include<climits>#include<cstdlib>using namespace std;//#define DEBUGconst int MAXMOD = 2520;typedef __int64 ll;ll f[20][50][MAXMOD+100];int h[MAXMOD+100];int num[20];ll xGcd(ll a, ll b){return (a % b == 0)? b:xGcd(b, a % b);}ll xLcm(ll a, ll b){return a / xGcd(a, b) * b;}ll dfs(int n, int lcm, int m, bool flag){#ifdef DEBUGcout<<"n="<<n<<"   flag"<<((flag)? 1:0)<<endl;#endifif ( !n ) return m % lcm == 0;if ( ~f[n][h[lcm]][m] && !flag ) return f[n][h[lcm]][m];int k = (flag? num[n]:9);#ifdef DEBUGcout<<"k="<<k<<endl;#endifll ans = 0;for (int i = 0; i <= k; i++){int cLcm = (i)? xLcm(lcm, i):lcm;int cM = (m * 10 + i) % MAXMOD;ans += dfs(n - 1, cLcm, cM, flag && (i == k));}if ( !flag ) f[n][h[lcm]][m] = ans;return ans;}ll getAns(ll a){#ifdef DEBUGcout<<"num="<<a<<endl;#endifint len = 0;while ( a ) num[++len] = a % 10, a /= 10;#ifdef DEBUGfor (int i = len; i; i--) cout<<num[i]<<"   ";cout<<endl;cout<<"dfs="<<dfs(len, 1, 0, true)<<endl;#endifreturn dfs(len, 1, 0, true);}int main(){//freopen("in.txt", "r", stdin);int cn = 0;for(int i = 1; i <= MAXMOD; i++) if ( MAXMOD % i == 0 ) h[i] = cn++;#ifdef DEBUGcout<<"cn="<<cn<<endl;#endifmemset(f, -1, sizeof(f));int T;scanf("%d", &T);while ( T-- ){ll l, r;scanf("%I64d%I64d", &l, &r);printf("%I64d\n", getAns(r) - getAns(l-1));}    return 0;}


0 0