首页 > 代码库 > BZOJ1853 SCOI2010 幸运数字 DFS+容斥原理

BZOJ1853 SCOI2010 幸运数字 DFS+容斥原理

题意:求[a,b]中含有6 8或因子含有6 8的数的个数

题解:

我们用容斥的思想,先求出1->b的方案数,再减去1->a-1的方案数
首先我们一遍DFS求出所有由6和8组成的数的数量
然后将搜索得到的所有数排序,如果存在a%b==0,那就把a给删了(打个标记)。
由于得到的数非常少,所以我们可以枚举乘积可能得到的数字num,那么就会有N/num个数含有num这个因子,因此ans+=N/num。
两个细节上的问题:
1.如果我们当前枚举得到的乘积为a,需要乘下一个数b搜索到下一层时,只需要传递lcm(a,b)即可
2.枚举得到的数一定会爆long long,要是有闲心可以去写高精……我上网搜了一下发现都是用double判精度之类很神奇的方法(至少我是没看懂……)。由于我懒到爆了,所以直接用自然溢出——如果枚举得到的数now<0,那肯定是发生自然溢出了,但问题在于如果第32位溢出后是0,那么now>0这样就得到了错解,但是数据非常的友善,我就这样AC了……

技术分享
#include <cmath>#include <cstdio>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;#define lcm(a,b) (a*b/gcd(max(a,b),min(a,b)))#define ll long longconst int MAXN=100000;ll a,b,cnt,num[MAXN],ans,tot;bool flag[MAXN];ll gcd(ll a,ll b){ return (!b?a:gcd(b,a%b));}bool cmp(ll a,ll b){ return a>b;}void DFS1(ll now,ll N){    if(now>N) return;    DFS1(now*10+6,N),DFS1(now*10+8,N);    if(now) num[++cnt]=now;}void DFS2(ll now,int deep,int c,ll N){    if(deep==cnt || now>N || now<0) return;//now<0 =>自然溢出 =>可行性剪枝    ll ret=0,type=(deep%2?1:-1);    if(now!=1) ans+=type*N/now;    for(int i=c;i<=cnt;i++)        if(!flag[i]) DFS2(lcm(now,num[i]),deep+1,i+1,N);}ll Slove(ll N){    ans=cnt=0;    memset(flag,0,sizeof(flag));    DFS1(0,N);    sort(num+1,num+cnt+1,cmp);    for(int i=1;i<=cnt;i++)        for(int j=i+1;j<=cnt;j++)            if(!(num[i]%num[j])){                flag[i]=1;                break;            }    DFS2(1,0,1,N);    return ans;}int main(){    cin >> a >> b;    cout << Slove(b)-Slove(a-1) << endl;    return 0;}
View Code

 

BZOJ1853 SCOI2010 幸运数字 DFS+容斥原理