数位DP专题

CF55D

这个题的核心要素是:某个数n被它的所有非零数位整除的同时,也是整除了所有非零数位的最小公倍数。所以在分析数位的同时需要照顾已经分析的所有非零数位的最小公倍数,和这些数位组成的这个数取余它的模。

所以dfs过程就有了三维数据:当前数位,余数,最小公倍数。然而余数和最小公倍数可能很大,在记忆化过程中较难实现,所以需要优化一下。可以发现1~9的最小公倍数为2520,如果一个数被2520整除,那么就可以断定无论如何都可以被它的每一位整除。但是数位的所有数字并不全是1~9,还可能有别的数字,所以只要取余数位最小公倍数为0的都可以认为满足条件。

至此,空间可以优化到20*2520*2520,但是这样仍然会空间超限。可以发现最后一维,也就是最小公倍数只有48种组成情况(因式分解),所以可以把它压成48个大小,再利用一个哈希数组使1~48各自都对应一个因数组成方案。

//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
//    BigInteger tmp = 0, si = 1;char c;    c = getchar();
//    while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
//    while (isdigit(c))
//    {tmp = tmp * 10 + c - '0';c = getchar();}
//    return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
//    if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
//    return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
//    if (x < 0)
//    {x = -x;putchar('-');}
//    if (x > 9) output(x / 10);
//    putchar(x % 10 + '0');
//    }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
ll dp[50][2530][50];
ll digits[500],pts=0;
map<ll, ll>mp;
namespace DigitsDp
{
	void preparation()
	{
		pts = 0;
		reset(digits, 0);
	}
	void div(ll x)
	{
		while (x)
		{
			digits[++pts] = x % 10;
			x /= 10;
		}
	}
	void getFactors()
	{
		ll cnt = 0;
		for (int i = 1; i <=2520; i++)
		{
			if (2520 % i == 0)
				mp[i] = ++cnt;
		}
	}
	ll gcd(ll a, ll b)
	{
		return b == 0 ? a : gcd(b, a%b);
	}
	ll Lcm(ll a, ll b)
	{
		return a * b / gcd(a, b);
	}
	ll dfs(ll current, ll mod, ll lcm, bool boundary)
	{
		if (current == 0)
		{
			//cout << current << " " << mod << " " << lcm << " " << boundary << endl;
			return mod % lcm == 0 ? 1 : 0;
		}
		if (boundary==false&&dp[current][mod][mp[lcm]]!=-1)
			return dp[current][mod][mp[lcm]];
		else
		{
			//cout << current << " " << mod << " " << lcm << " "<<boundary << endl;
			ll &tmp = dp[current][mod][mp[lcm]];
			ll limit = boundary == true ? digits[current]:9;
			ll ans = 0;
			for (int i = 0; i <= limit; i++)
			{
				ll nextNum = (mod*10+i) % 2520;
				ll nextlcm = lcm;
				if (i != 0)
					nextlcm = Lcm(lcm, i);
				ans += dfs(current - 1, nextNum, nextlcm, boundary&&i == limit);
			}
			if (boundary == false)
				tmp = ans;
			return ans;
		}
	}
};
int DETERMINATION()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值