题目链接:传送门
题目大意:
幸运数的定义:只包含6或8
近似幸运数:幸运数的倍数
问给定一个区间内近似幸运数的个数
题目思路:
我们从简单的开始思考,求100以内能被6,8,10整除的数的个数。
(100/6+100/8+100/10)-(100/24+100/30+100/40)+100/120
就是简单的容斥原理。
同样这道题也是,预处理出所有比他小幸运数之后容斥即可。
有几个点需要注意:
1.幸运数中可以删去既是幸运数也是近似幸运数的数,这种数是重复的
2.
此题数据范围是真正的10^10,于是我们需要一些方法来防止爆long long
就是这段if(((double)a[x]*tmp)<=r) dfs(x+1,y+1,a[x]*tmp);
因为a[x]*tmp可能超过long long,于是我们先用double存它的近似值,如果它小于r,则一定在long long范围内,就可以直接乘了
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=50000;
typedef long long ll;
ll a[maxn];
int cnt;
void dfs1(int dpth,ll s)
{
if(dpth==10)return;
a[cnt++]=s*10+6;
dfs1(dpth+1,s*10+6);
a[cnt++]=s*10+8;
dfs1(dpth+1,s*10+8);
}
int num;
ll b[maxn];
ll gcd(ll a,ll b)
{
if(b==0)return a;
return gcd(b,a%b);
}
ll l,r;
ll ans=0;
void dfs(int x,int y,ll z)
{
if(x>=num)
{
if(y&1)ans+=r/z-(l-1)/z;
else if(y)ans-=r/z-(l-1)/z;
return;
}
dfs(x+1,y,z);
ll tmp=z/gcd(b[x],z);
if(((double)b[x]*(double)tmp)<=r)
dfs(x+1,y+1,b[x]*tmp);
}
bool cmp(ll a,ll b)
{
return a>b;
}
int main()
{
cnt=0;
dfs1(0,0);
sort(a,a+cnt);
num=0;
for(int i=0;i<cnt;i++)
{
int ok=true;
for(int j=0;j<i;j++)
{
if(a[i]%a[j]==0)
{
ok=false;
break;
}
}
if(ok)b[num++]=a[i];
}
sort(b,b+num,cmp);
while(~scanf("%lld%lld",&l,&r))
{
ans=0;
dfs(0,0,1);
printf("%lld\n",ans);
}
return 0;
}