容斥+dfs--bzoj1853 [SCOI2010]幸运数字

传送门

第一眼以为是数位dp
但看到要求只能有6和8并且对每位上的数没什么要求
于是想到容斥
ans=选1个-选2个的lcm+选3个的lcm···
容斥的实现通常结合了dfs
先预处理出1-r范围内所有的真幸运数字
然后保留比r/2小的,也就是说比r/2大的都可以用这些来覆盖到
而且最后要从大到小排个序,使得lcm更快达到上界r
然后就是dfs啦,要记录当前是选几个,选到了哪里以及当前算出的lcm
(下面一段是hzwer的)
此题数据范围是真正的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<cstring>
#include<cmath>
#define LL long long
#define maxn 10005
using namespace std;
LL l,r,a[maxn],b[maxn],ans;
int numa,numb;
bool hs[maxn];

void init(int x,LL y){
    if(y>r) return;
    if(x) a[++numa]=y;
    init(x+1,y*10+6);
    init(x+1,y*10+8);
}
LL gcd(LL x,LL y) {return y==0?x:gcd(y,x%y);}
void dfs(int now,int ty,LL x){
    if(now>numb){
        if(ty&1) ans+=r/x-(l-1)/x;//容斥 
        else if(ty) ans-=r/x-(l-1)/x;
        return;
    }
    dfs(now+1,ty,x);
    LL tmp=x/gcd(a[now],x);
    if((double)a[now]*tmp<=r)//当前lcm比r大就不再搜 
        dfs(now+1,ty+1,a[now]*tmp);
}

int main(){
    scanf("%lld%lld",&l,&r);
    init(0,0);//预处理1-r的真幸运数字 
    sort(a+1,a+numa+1);
    for(int i=1;i<=numa;i++)
        if(!hs[i]){
            b[++numb]=a[i];
            for(int j=i+1;j<=numa;j++)//舍弃能被前面的数覆盖的  
                if(a[j]%a[i]==0) hs[j]=1;
        }
    for(int i=1;i<=numb;i++)
        a[numb-i+1]=b[i];//预处理出的幸运数字从大到小排序,使lcm能更快达到r的上界 
    dfs(1,0,1);
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值