BZOJ1853 [Scoi2010]幸运数字

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

 

 

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

 

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

 

正解:搜索+容斥原理

解题报告:

  这道题其实乍一看怎么都不像搜索题,但是仔细想想还是很有道理的,因为满足要求的幸运号码并不多,那么我可以预处理出所有幸运号码后容斥即可。

  但是容斥的复杂度是指数级,理论上不可能跑得过,不过很容易发现由于l、r只有1e10级别,那么我在容斥的时候不可能太大,也就是说顶多几个数相乘就会超过r,只需要剪枝减掉就可以了。

  有一个细节,在乘的时候有可能爆long long,那么我在乘之前就用double的近似值判断一下是不是已经爆了long long,就可以解决这个问题了。

 

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
using namespace std;
typedef long long LL;
const int MAXN = 100011; 
LL l,r,a[MAXN],ans,b[MAXN];
int tot,n;
bool vis[MAXN];
inline LL gcd(LL x,LL y){ if(y==0) return x; return gcd(y,x%y); }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline void make(LL x){
	if(x>r) return ;
	if(x>0) a[++tot]=x;
	make(x*10+6);
	make(x*10+8);
}

inline void dfs(int x,int now,LL val){
	if(x==n+1) {
		if(now==0||val==0) return ;
		if(now&1) ans+=r/val-(l-1)/val;
		else ans-=r/val-(l-1)/val;
		return ;
	}
	dfs(x+1,now,val);
	LL nn; if(now==0) { dfs(x+1,now+1,b[x]); return ; }
	nn=val/gcd(val,b[x]);
	if((double)b[x]*nn>r) return ;
	dfs(x+1,now+1,b[x]*nn);
}

inline void work(){
	scanf("%lld%lld",&l,&r); make(0); sort(a+1,a+tot+1);
	for(int i=1;i<=tot;i++) if(!vis[i]) { b[++n]=a[i]; for(int j=i+1;j<=tot;j++) if(a[j]%a[i]==0) vis[j]=1; }
	for(int i=1;i<=n/2;i++) swap(b[i],b[n-i+1]);
	dfs(1,0,0);
	printf("%lld",ans);
}

int main()
{
    work();
    return 0;
}

  

转载于:https://www.cnblogs.com/ljh2000-jump/p/6211670.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值