【bzoj1853】【scoi2010】幸运数字

本文介绍了一个关于寻找“近似幸运数字”的算法竞赛题目。幸运数字仅由6和8组成,而近似幸运数字则是这些幸运数字的倍数。文章详细解析了如何通过递归方法和容斥原理来高效计算指定区间内的近似幸运数字数量。

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

1853: [Scoi2010]幸运数字

Time Limit: 2 Sec   Memory Limit: 64 MB
Submit: 2269   Solved: 829
[ Submit][ Status][ Discuss]

Description

在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。

Input

输入数据是一行,包括2个数字a和b

Output

输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数

Sample Input

【样例输入1】
1 10
【样例输入2】
1234 4321

Sample Output

【样例输出1】
2
【样例输出2】
809

HINT

【数据范围】
对于30%的数据,保证1 < =a < =b < =1000000
对于100%的数据,保证1 < =a < =b < =10000000000

Source

[ Submit][ Status][ Discuss]

方法1:暴力容斥,在lcm>b时剪枝,复杂度O(玄学)

方法2:前若干个容斥(我选不超过100000的所有数),后面直接暴力,复杂度O(玄学)(比上面的低)

代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<map>
#define lbt(i) ((i)&(-(i)))
using namespace std;
typedef long long ll;

const int MX=20100,A=(1<<9)-1;
map<ll,bool>vis;
ll q[MX],s[MX];
ll ans,b,u;
int sc;
inline ll lcm(ll a,ll b)
{
	ll res=a*b;
	if(a<b)swap(a,b);
	for(;b;b^=a^=b^=a%=b);
	return res/a;
}
inline void dfs(ll chosen,ll k,int K)
{
	if(k>u)return;
	ans+=(K?(u/k-(b-1)/k):-(u/k-(b-1)/k));
	for(ll i=1,j=1;i<lbt(chosen);i<<=1,j++)
	dfs(chosen|i,lcm(k,s[j]),K^1);
}
ll calc()
{
	int h=0,t=0;
	q[++t]=6;q[++t]=8;
	while(h<t)
	{
		ll r=q[++h];
		if(r>u/100000){h--;break;}
		q[++t]=r*10+6;
		q[++t]=r*10+8;
		bool flg=true;
		for(int i=1;i<=sc;i++)
			if(r%s[i]==0){flg=false;break;}
		if(flg)s[++sc]=r;
	}
	for(int i=0;i<sc;i++)dfs(1LL<<i,s[i+1],1);
	while(h<t)
	{
		ll r=q[++h];
		if(r>u)break;
		q[++t]=r*10+6;
		q[++t]=r*10+8;
		bool flg=true;
		for(int i=1;i<=sc;i++)
			if(r%s[i]==0){flg=false;break;}
		if(flg){
			for(ll tmp=(b-1)/r*r+r;tmp<=u;tmp+=r)
			{
				bool flg1=true;
				for(int i=1;i<=sc;i++)
					if(tmp%s[i]==0){flg1=false;break;}
				if(flg1&&!vis[tmp]){
					vis[tmp]=true;ans++;
				}
			}
		}
	}
	return ans;
}
int main()
{
	scanf("%lld%lld",&b,&u);
	printf("%lld\n",calc());
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值