CF55D Beautiful numbers 题解

题目传送门(原OJ)
题目传送门(洛谷(有翻译))

首先看到数据范围, 9 e 18 9e18 9e18,再看题面,很明显的数位DP。

需要注意的地方:

0 0 0 可以整除所有数。。。
答案可能会大于 18 18 18 位,所以 i n t int int 存不下!!!

分析

然后我们先确定 d p dp dp 数组的下标,显然是一个三维的数组 —— d p [ p o s ] [ r a r ] [ s t a ] dp[pos][rar][sta] dp[pos][rar][sta]

我们用 p o s pos pos 表示当前位数, r a r rar rar 压缩一个状态,当先数位上有哪些数字,用 s t a sta sta 来表示原数。

但很显然,直接存 s t a sta sta 是存不下的,于是我们想到了 m o d mod mod,那么我们将模数设为什么好呢?—— 1   9 1~9 1 9 l c m lcm lcm 也就是 2520 2520 2520

于是我定义了 d p [ 20 ] [ 1024 ] [ 2521 ] dp[20][1024][2521] dp[20][1024][2521],经过计算后发现大概是 51 , 630 , 080 51,630,080 51,630,080,也就是说只要开 i n t int int 就不会炸空间,于是我就很高兴的开了 i n t int int 。。。

“于是它飞快地 WA 掉了”—— 某dalao

后来发现了答案可能很大。。。其实将 2520 2520 2520 进行离散化,但我 实在太弱了。。。没想到 发现对于 0 0 0 1 1 1 都是可以不计入状态中的,因为任意一个数(非 0 0 0 )都可以被它们整除。所以我们就可以将第二位的上限改为 512 512 512,大小变成了原来的 1 2 \frac{1}{2} 21 我们就可以开 l o n g l o n g long long longlong 啦。 #define int long long

然后我们确定 d f s dfs dfs 中的形参,很显然就是 p o s , r a r , s t a , l i m i t pos,rar,sta,limit posrarstalimit l i m i t limit limit 表示是否到达上限。至于前导零的判断,本题并不需要。

那么我们就有两种转移,当 i ≤ 1 i \leq 1 i1 时:( u p up up 表示上限)

tem += dfs(pos - 1, rar, (sta * 10 + i) % MOD, (i == up) && limit);

而当 2 ≤ i 2 \leq i 2i 时:( 不会打 $ latex $ 的大于等于 QAQ

tem += dfs(pos - 1, rar | (1 << (i - 2)), (sta * 10 + i) % MOD, (i == up) && limit);

下面放上代码(实测 A C AC AC

#include<bits/stdc++.h>
#define int long long
#define inc(i) (++ (i))
#define dec(i) (-- (i))
#define Rep(i, a, b) for(register int i = (a) , i##Limit = (b) ; i <= i##Limit ; inc(i))
#define Dep(i, a, b) for(register int i = (a) , i##Limit = (b) ; i >= i##Limit ; dec(i))

using namespace std;

const int MOD = 2520;
int a, b, T;
int res[21], dp[20][512][2521];

int dfs(int pos, int rar, int sta, bool limit) {
	if(!pos) {
		int num = 1;
		while(rar) {
			inc(num);
			if(rar & 1 && sta % num != 0) return 0;
			rar >>= 1;
		}
		return 1;
	}
	if(dp[pos][rar][sta] != -1 && !limit) return dp[pos][rar][sta];
	int up, tem = 0;
	if(limit) up = res[pos];
	else up = 9;
	Rep(i,0,up) {
		if(i <= 1) tem += dfs(pos - 1, rar, (sta * 10 + i) % MOD, (i == up) && limit);
		else tem += dfs(pos - 1, rar | (1 << (i - 2)), (sta * 10 + i) % MOD, (i == up) && limit);
	}
	if(!limit) dp[pos][rar][sta] = tem;
	return tem;
}

int solve(int x) {
	int pos = 0;
	while(x) {
		res[inc(pos)] = x % 10;
		x /= 10; 
	}
	return dfs(pos, 0, 0, 1);
}

signed main()
{
	cin>>T;
	memset(dp, -1, sizeof(dp));
	while(T --) {
		cin>>a>>b;
		printf("%lld\n", solve(b) - solve(a - 1));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值