【bzoj2393】【Cirno的完美算数教室】容斥原理的剪枝应用

这里写图片描述
(上不了p站我要死了,侵权度娘背锅)

在用容斥定理时,常常会用到dfs的形式,如果枚举完所有的情况可能会超时,其剪枝的优化很是重要。

Description
~Cirno发现了一种baka数,这种数呢~只含有2和⑨两种数字~~
现在Cirno想知道~一个区间中~~有多少个数能被baka数整除~
但是Cirno这么天才的妖精才不屑去数啦
只能依靠聪明的你咯。
Input
一行正整数L R
( 1 < L < R < 10^10)
Output
一个正整数,代表所求的答案
Sample Input
1 100
Sample Output
58

像这种筛数的题很容易想到用容斥原理。
一个数的倍数-两个数的倍数+三个数的倍数……

我们发现,baka数一共有2^10个,但是其中有很多成倍数关系。于是就可以筛去不必要的数(倍数关系的数)。这要就可以减少很多数字了。

dfs时也有剪枝优化,如果此时的lcm已经大于上限,就可以return了。如果先枚举大的,可以减更多的枝。

排序前与排序后的对比
这里写图片描述

主要是练一下模板

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long 
#ifdef WIN32
#define RIN "%I64d"
#else
#define RIN "%lld"
#endif

ll L,R;
ll num[1<<10],tp[1<<10],cnt1,cnt,ans;

void get_num(ll x){
    if(x>R) return;
    if(x*10+9<=R) tp[++cnt1]=x*10+9;//,printf("%d ",x*10+9);
    get_num(x*10+9);
    get_num(x*10+2);
}
ll gcd(ll a,ll b){
    if(b==0) return a;
    return gcd(b,a%b);
}
void dfs(int step,ll now,ll lim,int state){
    if(now>lim||step>cnt) return;
    ll tmp=now*num[step]/gcd(now,num[step]);
    ans+=lim/tmp*state;
    dfs(step+1,tmp,lim,-state);
    dfs(step+1,now,lim,state);
}
ll solve(ll x){
    ans=0;
    dfs(1,1,x,1);
    return ans;
}
bool cmp(const ll &a,const ll &b){
    return a>b;
}
int main(){
    scanf(RIN,&L);
    scanf(RIN,&R);
    tp[++cnt1]=2;
    get_num(0);
    for(int i=1;i<=cnt1;i++){
        bool bj=0;
        for(int j=1;j<=cnt;j++)
            if(tp[i]%num[j]==0){
                bj=1;break;
            } 
        if(!bj) num[++cnt]=tp[i];
    }
    sort(num+1,num+cnt+1,cmp);
    ll ansl=solve(L-1);
    ll ansr=solve(R);
    printf(RIN"
",ansr-ansl);
    return 0;
}
原文地址:https://www.cnblogs.com/LinnBlanc/p/7763078.html