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;
}
posted @ 2019-02-25 19:05  风浔凌  阅读(182)  评论(0编辑  收藏  举报