BZOJ1853 [Scoi2010]幸运数字 容斥原理

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1853


题意概括

  求一个区间范围内,近似幸运数字的个数。

  定义:

  幸运数字:仅由6或者8组成的数字。

  近似幸运数字:幸运数字的正整数倍。


 

题解

  我们发现幸运数字很少。

  然后,我们考虑容斥。

  我们发现原来的大整数除几次机会很小。所以记忆化dfs容斥,中途跳出。

  这样可以节省很多时间。

  然后居然过去了。


 

代码

#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
typedef unsigned long long LL;
int totluck;
LL L,R,ans,luck[2050];
bool alive[2050];
void df(LL x){
	if (x>R)
		return;
	if (x>0)
		luck[++totluck]=x;
	df(x*10+6);
	df(x*10+8);
}
LL gcd(LL a,LL b){
	return b?gcd(b,a%b):a;
}
void dfs(int num,int pos,LL Lcm){
	if (pos>totluck){
		if (num==0)
			return;
		if (num%2==1)
			ans+=R/Lcm-(L-1)/Lcm;
		else
			ans-=R/Lcm-(L-1)/Lcm;
		return;
	}
	dfs(num,pos+1,Lcm);
	LL g=Lcm/gcd(Lcm,luck[pos]);
	if (1.0*g*luck[pos]<=R)
		dfs(num+1,pos+1,g*luck[pos]);
}
bool cmp(LL a,LL b){
	return a>b;
}
int main(){
	scanf("%llu%llu",&L,&R);
	totluck=0;
	df(0);
	sort(luck+1,luck+totluck+1,cmp);
	memset(alive,true,sizeof alive);
	for (int i=1;i<totluck;i++)
		for (int j=i+1;j<=totluck&&alive[i];j++)
			if (luck[i]%luck[j]==0)
				alive[i]=0;
	int to=0;
	for (int i=1;i<=totluck;i++)
		if (alive[i])
			luck[++to]=luck[i];
	totluck=to;
	ans=0;
	dfs(0,1,1);
	printf("%llu\n",ans);
	return 0;
}

  

posted @ 2017-09-10 13:08  zzd233  阅读(291)  评论(0编辑  收藏  举报