题目传送门 - 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;
}