BZOJ1853 容斥DFS+剪枝

版权声明:欢迎转载 https://blog.csdn.net/animalcoder/article/details/82504140

36.BZOJ1853容斥DFS+剪枝

题意:问l,r内有多少个数满足:是 只有6或8组成的数 的倍数 l,r<=1e10

比如12,48,6,68均满足要求

//BZOJ1853 题意:问l,r内有多少个数满足:是 只有6或8组成的数 的倍数 l,r<=1e10

//思路:先预处理出 1e10内只有6 8组成的数(大概2500个)

//然后2500内筛掉一些数是别的数的倍数,这些不影响(剩500个)

//剩下的数很多在1e9到1e10之间,所以暴力从大到小容斥,lcm大于r就可以剪枝(强) 

//注意爆longlong的处理,两种方法都能过,看下面注释

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[5505];
ll b[5505];
int up=0;int nn=0;
bool vis[5505];
inline ll gcd(ll a,ll b){return a==0?b:gcd(b%a,a);}
inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
void geta(ll x,ll r)
{
    if(x<=r&&x>=0)
    {
        if(x)
        a[++up]=x;
        geta(1ll*x*10+6,r);
        geta(1ll*x*10+8,r);
    }
    return ;        
} 
ll ans=0;int ttt=0;
void dfs(int x,ll sum,int cnt,int nn,ll l,ll r)
{
    if(x==nn+1)
    {
        if(sum==1)return ;
        if(cnt&1)ans+=r/sum-(l-1)/sum;
        else ans-=r/sum-(l-1)/sum;
        return ;
    }   
    dfs(x+1,sum,cnt,nn,l,r);
    //注意tmp可能爆longlong,可以这样写或者转double 
    ll tmp=lcm(sum,a[x]);
    if(tmp>0&&tmp<=r)//强剪枝 
        dfs(x+1,tmp,cnt^1,nn,l,r);
    return ;
}
int main()
{
    ll l,r;scanf("%lld %lld",&l,&r);ans=0;
    geta(0,r);
    sort(a+1,a+1+up);
    //for(int i=1;i<=up;i++)printf("%lld\n",a[i]);
    for(int i=1;i<=up;i++)//去掉多余的数,减少枚举量 
    {
        if(vis[i]==0)
        {
            b[++nn]=a[i];
            for(int j=i;j<=up;j++)
                if(a[j]%a[i]==0)vis[j]=1;
        }   
    }   
    //printf("%d\n",nn);
    for(int i=1;i<=nn;i++)
        a[nn-i+1]=b[i];
    dfs(1,1,0,nn,l,r);
    printf("%lld\n",ans);
} 

猜你喜欢

转载自blog.csdn.net/animalcoder/article/details/82504140