zoukankan      html  css  js  c++  java
  • BZOJ 1853 幸运数字(容斥原理+dfs)

    题意:求闭区间内能被6和8组成的数字整除的数目。n<=1e11.

    我们可以预处理出这些6和8组成的数字,大概2500个,然后排除一些如88,66的情况。这样大概还剩下1000个。

    转化为[0,r]和[0,l-1]的问题,显然需要运用容斥原理。ans=n/6+n/8+n/68+...+...-n/lcm(6,8)-n/lcm(6,68)......

    因此用dfs即可计算出来,这样一看复杂度好像是2^1000的样子,但是注意到lcm增长的很快,如果lcm>n那么显然之后的这些情况就可以忽略了。

    这就是一个强有力的剪枝。

    另外从大到小dfs要比从小到大dfs要好。大概常数小?

    # include <cstdio>
    # include <cstring>
    # include <cstdlib>
    # include <iostream>
    # include <vector>
    # include <queue>
    # include <stack>
    # include <map>
    # include <set>
    # include <cmath>
    # include <algorithm>
    using namespace std;
    # define lowbit(x) ((x)&(-x))
    # define pi acos(-1.0)
    # define eps 1e-9
    # define MOD 1024523
    # define INF 1000000000
    # define mem(a,b) memset(a,b,sizeof(a))
    # define FOR(i,a,n) for(int i=a; i<=n; ++i)
    # define FO(i,a,n) for(int i=a; i<n; ++i)
    # define bug puts("H");
    # define lch p<<1,l,mid
    # define rch p<<1|1,mid+1,r
    # define mp make_pair
    # define pb push_back
    typedef pair<int,int> PII;
    typedef vector<int> VI;
    # pragma comment(linker, "/STACK:1024000000,1024000000")
    typedef long long LL;
    int Scan() {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void Out(int a) {
        if(a<0) {putchar('-'); a=-a;}
        if(a>=10) Out(a/10);
        putchar(a%10+'0');
    }
    const int N=500005;
    //Code begin...
    
    LL num[2505], pos1, pos2, p[15], mark[1005];
    void init(){
        p[0]=1; FOR(i,1,10) p[i]=p[i-1]*10;
        int l=1, r=2, tmpl, tmpr;
        num[++pos1]=6; num[++pos1]=8;
        FOR(i,2,10) {
            tmpl=r+1;
            FOR(j,l,r) num[++pos1]=6*p[i-1]+num[j];
            FOR(j,l,r) num[++pos1]=8*p[i-1]+num[j];
            tmpr=pos1;
            l=tmpl; r=tmpr;
        }
        FOR(i,1,pos1) {
            int flag=true;
            FO(j,1,i) if (num[i]%num[j]==0) {flag=false; break;}
            if (flag) mark[++pos2]=num[i];
        }
        mark[++pos2]=1e16;
    }
    LL dfs(int pos, int flag, LL x, LL cheng){
        if (pos<=0) return 0;
        LL res=0;
        res+=dfs(pos-1,flag,x,cheng);
        LL tmp=__gcd(cheng,mark[pos]);
        if (cheng/tmp<=(double)x/mark[pos]) {
            LL tt=cheng/tmp*mark[pos];
            res+=dfs(pos-1,flag^1,x,tt);
            res+=(flag?x/tt:-x/tt);
        }
        return res;
    }
    LL sol(LL x){
        for (int i=pos2; i>=1; --i) if (mark[i]<=x) return dfs(i,1,x,1);
        return 0;
    }
    int main ()
    {
        init();
        LL a, b;
        scanf("%lld%lld",&a,&b);
        printf("%lld
    ",sol(b)-sol(a-1));
        return 0;
    }
    View Code
  • 相关阅读:
    矩阵乘法(二):利用矩阵快速幂运算完成递推
    更改codeblock编译后程序的图标
    如何在VS2008下使用FLTK
    Python type() 函数
    Python range() 函数用法
    Python len()方法
    Python filter() 函数
    Python bool() 函数
    数据类型
    JAVA标识符
  • 原文地址:https://www.cnblogs.com/lishiyao/p/6796104.html
Copyright © 2011-2022 走看看