51nod 1232 完美数 | 数位DP
51nod 1232 完美数
题面
如果一个十进制数的每一位都是它的因数,则称这个数为完美数。
给出L, R(L, R <= 1e18),求出[L, R]中完美数的个数。
题解
做这道题,我才了解数位DP的套路写法……下次做数位DP应该就不会绞尽脑汁处理写法上的问题了。
dp[i][j][k]表示长度为i,模2560(1~9的LCM)得j,所有位上的数的LCM为k的数的个数。
它包括了所有的长度为i的数,例如i=5时,它要考虑的是10000~99999之间所有的数。
这里的k理论上也是一个小于等于2560的数,但这样太大了,需要离散化一下。打表发现1~9不同组合的LCM只有48个,很适合离散化。
数位DP的套路:用一个记忆化的dfs解决问题。详见代码。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define INF 0x3f3f3f3f
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
bool read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
else if(c == EOF) return 0;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
return 1;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 2530, M = 50;
int gcd(int a, int b){ return b ? gcd(b, a % b) : a; }
int lcm(int a, int b){ return a / gcd(a, b) * b; }
int f[N], g[]={0,1,2,3,4,5,6,7,8,9,10,12,14,15,18,20,21,24,28,30,35,36,40,42,45,56,60,63,70,72,84,90,105,120,126,140,168,180,210,252,280,315,360,420,504,630,840,1260,2520};
ll T, L, R, num[20];
ll dp[20][N][M];
void init(){
for(int i = 1; i <= 48; i++)
f[g[i]] = i;
memset(dp, -1, sizeof(dp));
}
ll dfs(int i, int j, int k, bool flag){
if(!i) return j % g[k] == 0;
if(!flag && dp[i][j][k] != -1) return dp[i][j][k];
ll res = 0, lim = flag ? num[i] : 9;
for(int x = 0; x <= lim; x++){
int newk = x ? f[lcm(g[k], x)] : k;
res += dfs(i - 1, (j * 10 + x) % 2520, newk, flag && x == lim);
}
return flag ? res : dp[i][j][k] = res;
}
ll solve(ll x){
int len = 0;
while(x) num[++len] = x % 10, x /= 10;
return dfs(len, 0, 1, 1);
}
int main(){
init();
read(T);
while(T--){
read(L), read(R);
write(solve(R) - solve(L - 1)), enter;
}
return 0;
}
本文作者:胡小兔
博客地址:http://rabbithu.cnblogs.com
博客地址:http://rabbithu.cnblogs.com