SCOI2010 幸运数字
题目链接:戳我
lcm相关 容斥+搜索剪枝
选一个数的近似幸运号码,但是这样子可能有两个或多个的数的倍数相同——所以考虑容斥——我们把两个的减去。但是这样一来是三个数的lcm的数的就没有计算到,我们再加上它。。。。。。
trick1——把合法的数先预处理出来,方便之后操作。
trick2——我们从小到大判断,如果一个数已经是前面一个数的倍数了,就无需记录它了,反正它的近似幸运数字都被包含于那个数字的近似幸运数字。
trick3——如果当前的lcm到达了上届(也就是题目中的B)我们就无需再搜索了(显然吧)
trick4——从大到小暴搜,可以使得lcm更快地达到上届。
最后,我们不需要先计算出[1,r]和[1,l-1]的,然后再相减计算答案。对于一个数x,区间[A,B]内它的倍数其实就是\(\lfloor\frac{B}{x}\rfloor+\lceil\frac{A}{x}\rceil+1\)
qwqwqwq代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 100010
using namespace std;
int tot,n;
int done[MAXN];
long long A,B,ans;
long long num[MAXN],pre[MAXN];
inline void init(int cnt,int limit,long long val,long long base)
{
//printf("cnt=%d limit=%d val=%lld base=%lld\n",cnt,limit,val,base);
if(cnt>limit) {pre[++tot]=val;return;}
init(cnt+1,limit,val+6*base,base*10);
init(cnt+1,limit,val+8*base,base*10);
}
inline bool cmp(long long x,long long y){return x>y;}
inline long long gcd(long long x,long long y){return y==0?x:gcd(y,x%y);}
inline long long calc(long long x)
{
long long cur_ans=0;
cur_ans=B/x-A/x-(A%x!=0)+1;
return cur_ans;
}
void search(int dep,int cnt,long long val)
{
if (val>B) return;
if (dep>n)
{
if (cnt==0) return;
ans+=calc(val)*((cnt&1)?1:-1);
return;
}
search(dep+1,cnt,val);
long long tmp=val/gcd(val,num[dep]);
if(1.0*tmp*num[dep]<=B)
search(dep+1,cnt+1,tmp*num[dep]);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
freopen("ce.out","w",stdout);
#endif
cin>>A>>B;
for(int i=1;i<=10;i++) init(1,i,0,1);
for(int i=1;i<=tot;i++)
{
if(!done[i]) num[++n]=pre[i];
for(int j=i+1;j<=tot;j++)
if(pre[j]%pre[i]==0) done[j]=1;
}
sort(&num[1],&num[n+1],cmp);
search(1,0,1);
cout<<ans<<endl;
return 0;
}