Codeforces 55D Beautiful numbers

本文探讨了如何解决Codeforces竞赛中特定类型的题目,即求解[a,b]区间内能被其所有非零数位整除的整数。通过引入状态压缩和最小公倍数(LCM)的概念,简化了状态空间,使用记忆化搜索提高了效率。介绍了从简单的模运算到状态转移的详细过程,并展示了如何利用已计算的状态来加速后续的计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://codeforces.com/contest/55/problem/D

题目简述:求[a,b]区间内整数n,n能被他所有非零数位上的数整除。


看见整除,肯定想的就是%i=0,所以要记他%1~9的余数,但这明显是不科学的。。

于是乎,只需要记下1~9的LCM,LCM=2520,这样状态就小了很多了,到最后再分开模。

但是我怎么知道他有1~9中的哪些?。。可以考虑状压,其实挺好。

但还有更好的方法,就是再记录一个选中的数的LCM,刚刚%2520的数再来模这个LCM如果是0那么肯定就对了。反之亦然。

1~9选出一些数的LCM其实只有48个,这样复杂度又压了很多。

状态就是F[i][MOD][LCM]


记忆化搜索真心很无脑啊,随便搞搞,边界条件设一下就等他自己跑就行了。。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
typedef long long LL;
const int MOD=2520;
int LCM[2521],num=0;
LL f[20][2521][49],a[20];
LL pow[20];
int Gcd(int a,int b){return !b?a:Gcd(b,a%b);}
int Lcm(int a,int b){return a*b/Gcd(a,b);}
void Prep(){
	memset(f,-1,sizeof f);
	pow[1]=1;
	rep(i,2,20) pow[i]=pow[i-1]*10%MOD;
	rep(i,1,MOD) if (MOD%i==0) LCM[i]=++num;
}
LL dfs(int pos,int mod,int lcm,bool lim){
	if (pos==0) return mod%lcm==0;
	if (!lim&&~f[pos][mod][LCM[lcm]]) return f[pos][mod][LCM[lcm]];
	int top=lim?a[pos]:9;LL res=0;
	rep(i,0,top){
		if (i==0) res+=dfs(pos-1,mod,lcm,lim&&i==top);
		else{
			int M=(mod+pow[pos]*i)%MOD;
			res+=dfs(pos-1,M,Lcm(lcm,i),lim&&i==top);
			}
		}
	return lim?res:(f[pos][mod][LCM[lcm]]=res);
}
LL count(LL x){
	int len=0;
	while (x) a[++len]=x%10,x/=10;
	return dfs(len,0,1,1);
}
int main(){
	Prep();
	int T;scanf("%d",&T);
	while (T--){
		LL l,r;
		scanf("%I64d%I64d",&l,&r);
		printf("%I64d\n",count(r)-count(l-1));
		}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值