51nod 1232:完美数

题目来源:  胡仁东
基准时间限制:2 秒 空间限制:131072 KB 分值: 160  难度:6级算法题
 收藏
 关注
如果一个数能够被组成它的各个非0数字整除,则称它是完美数。例如:1-9都是完美数,10,11,12,101都是完美数,但是13就不是完美数(因为13不能被数字3整除)。
现在给定正整数x,y,求x和y之间(包含x和y的闭区间)共有多少完美数。

题目作者为: hrdv
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000)
第2 - T + 1行:每行2个数,X, Y中间用空格分割。(1 <= X <= Y <= 10^18)
Output
输出共T行,对应区间中完美数的数量。
Input示例
2
1 9
12 15
Output示例
9
2

一个数整除所有位置上的数,也就是整除这些数的lcm,最多2520。然后记录每一个数对2520的余数,数位dp。另外,lcm的数量不是很多,离散化记录。

代码:

//#pragma comment(linker, "/STACK:655360000")  
#pragma warning(disable:4996)
#include <iostream>
#include <functional>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <deque>
#include <set>
#include <map>
using namespace std;
typedef long long ll;

#define INF 0x333f3f3f
#define repp(i, n, m) for (int i = n; i <= m; i++)
#define rep(i, n, m) for (int i = n; i < m; i++)
#define sa(n) scanf("%d", &(n))

const ll mod = 2520;
const int maxn = 1e5 + 5;
const double PI = acos(-1.0);

int dig[20], ha[2550];
ll dp[20][50][2550];

ll gcd(ll x, ll y)
{
	if (y == 0)
		return x;
	else
	{
		return gcd(y, x%y);
	}
}

void init()
{
	int i, j, k, cnt;
	memset(dp, -1, sizeof(dp));

	cnt = 0;
	for (i = 1; i <= 2520; i++)
	{
		if (mod%i == 0)
		{
			cnt++;
			ha[i] = cnt;
		}
	}
}

ll dfs(ll n, ll top, ll lcm, ll mo)
{
	if (n <= 0)
		return mo%lcm == 0;
	if (!top&&dp[n][ha[lcm]][mo] != -1)
		return dp[n][ha[lcm]][mo];
	int end = (top == 1) ? dig[n] : 9;
	int i, j, k;
	ll ans = 0;
	for (i = 0; i <= end; i++)
	{
		int m = (mo * 10 + i) % mod;
		int lcm_tmp;
		if (i != 0)
			lcm_tmp = lcm / gcd(lcm, i)*i;
		else
			lcm_tmp = lcm;
		ans += dfs(n - 1, top&(i == end), lcm_tmp, m);
	}
	if(!top)
		dp[n][ha[lcm]][mo] = ans;
	return ans;
}

ll solve(ll x)
{
	int i, j, k, cnt = 0;
	memset(dig, 0, sizeof(dig));
	while (x)
	{
		cnt++;
		dig[cnt] = x % 10;
		x /= 10;
	}
	ll r = dfs(cnt, 1, 1, 0);
	return r;
}

int main() 
{
	init();
	int t;
	sa(t);
	while (t--)
	{
		ll x, y;
		scanf("%lld%lld", &x, &y);
		printf("%lld\n", solve(y) - solve(x - 1));
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值