[题解]CF55D Beautiful Numbers

CF55D Beautiful Numbers

打出暴搜后有些茫然,不知道该怎么优化才好,看了题解才豁然开朗。

简单说下暴搜的思路:参数有\(pos,limit,lcm,num\)。其中\(lcm\)表示到\(pos+1\)位,所有非\(0\)位的\(lcm\)是多少;\(num\)表示填到\(pos+1\)位的整个数是多少。然后在\(pos=0\)时判断\(lcm\)是否整除\(num\),是则返回\(1\),否则返回\(0\),然后一层层累加。

优化思路其实很简单:
显然直接用\(f[pos][num][lcm]\)来记忆化,无论时间还是空间都是过不去的。
但我们知道所有非\(0\)位整除\(num\),就等价于它们的\(lcm\)整除\(num\)
我们想知道\(num\)是否被\(2\)整除,那么我们关注它\(mod\ 2\)的值是否为\(0\)
我们想知道\(num\)是否被\(6\)整除,那么我们关注它\(mod\ 6\)的值是否为\(0\)
……
那么我们想知道\(num\)是否被\(a_1,a_2,…,a_k\)整除,那么我们只需要关注它\(mod\ lcm\{a_1,a_2,…,a_k\}\)的值是否为\(0\)

那么我们只需要记录\(num\ mod\ lcm\{1,2,…,9\}\)的值即\(num\ mod\ 2520\)即可。
\(f[pos][num][lcm]\)空间共\(20*2520*2520\)。而250MB的空间限制还是不允许我们开那么大。怎么继续优化呢?


注意到\(lcm\)中我们能用到的一共就那么几个。我们知道\(2520\)一共有\(48\)个因数。而我们\(1\sim 9\)能凑出的数的个数肯定比\(48\)少了。因此我们把质因数离散化一下,第三维开\(50\)绰绰有余。

我的代码和离散化的意思差不多,大概是现读现存的感觉,用map存下来每个质因数存在哪个位置上。

(不知道为什么用了unsigned long long,其实不需要)

点击查看代码
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
int t,l,r,a[30],f[30][2520][50];
bool vis[30][2520][50];
map<int,int> ma;
int val[50],cnt;
int __lcm(int a,int b){
	return a/__gcd(a,b)*b;
}
int dfs(int pos,bool limit,int lcm,int num){
	if(pos==0){
		if(num%lcm==0) return 1;
		return 0;
	}
	if(!limit&&ma.find(lcm)!=ma.end()&&vis[pos][num][ma[lcm]]) return f[pos][num][ma[lcm]];
	int rig=limit?a[pos]:9,ans=0;
	for(int i=0;i<=rig;i++){
		ans+=dfs(pos-1,limit&&i==rig,i?__lcm(lcm,i):lcm,(num*10+i)%2520);
	}
	if(!limit){
		if(ma.find(lcm)==ma.end()) ma[lcm]=++cnt,val[cnt]=lcm;
		f[pos][num][ma[lcm]]=ans,vis[pos][num][ma[lcm]]=1;
	}
	return ans;
}
int solve(int x){
	int len=0;
	while(x){
		a[++len]=x%10;
		x/=10;
	}
	return dfs(len,1,1,0);
}
signed main(){
	cin>>t;
	memset(vis,0,sizeof vis);
	while(t--){
		cin>>l>>r;
		cout<<solve(r)-solve(l-1)<<endl;
	}
	return 0;
}
posted @ 2024-04-14 11:19  Sinktank  阅读(8)  评论(0编辑  收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.