版权声明:欢迎转载 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);
}