• P2567 [SCOI2010]幸运数字 [容斥+有技巧的搜索]


    幸运数字


    Descriptionmathcal{Description}
    在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。

    现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。


    最初想法
    预处理出所有的 幸运数字, 存到 A[]A[] 数组里, 共有 cntcnt 个.

    [1,x][1,x]含有 F[i]F[i] 个第 ii 个幸运数字的倍数,
    枚举 AiA_i 的倍数, 若 存在 AiAjA_i|A_j, 则 F[i]=F[j]F[i]-=F[j].
    最后 Ans=i=1cntF[i]Ans=sum_{i=1}^{cnt}F[i].

    但是这个算法连样例都过不去.

    上方算法好蠢 …

    错因: 存在 Lcm(Ai,Aj)AiLcm(A_i,A_j)|A_i, 但是 由于 Lcm(Ai,Aj)Lcm(A_i,A_j) 并不一定在 A[]A[] 中, 于是F[i]F[i] 没有减去 Lcm(Ai,Aj)Lcm(A_i,A_j) 对应的 FF 值 .


    正解部分
    老实地 容斥, 大力地 剪枝 .

    • 1:,使DFSbreak.优化1: 按从大到小排序,使得DFS尽早break.
    • 2:AiAj,Aj优化2: 对于A_i|A_j,去掉A_j无影响.
    • 3:Ai>b/3,,color{red}{优化3:对于A_i>b/3, 其无法与其它数字结合, 直接计入答案}

    • 1:Lcm, return剪枝1: 若当前的 Lcm 大于右端点, return

    注意DFSDFS出来的A[]A[]数组并不是有序的, 这是因为 DFSDFS 的回溯过程使得数据梯度出现向下的陡坡.

    求解 LcmLcm 时要小心爆 long longlong long, 要转化为 doubledouble 进行运算 .

    总结: 对暴搜的优化主要包括
    1. 使搜索规模消减: 体现在 优化2,3 .
    2. 使搜索过程尽早结束, 体现在 优化1,剪枝1 .

    实现部分
    没什么好说的 …

    #include<cstdio>
    #include<algorithm>
    #define reg register
    typedef long long ll;
    
    const int maxn = 40005;
    
    int cnt;
    
    ll a;
    ll b;
    ll Tmp_1;
    ll A[maxn];
    ll B[maxn];
    
    bool Used[maxn];
    
    ll Gcd(ll a, ll b){ return !b?a:Gcd(b, a%b); }
    ll Lcm(ll a, ll b){ return a/Gcd(a, b) * b; }
    
    void DFS(ll sum){
            if(sum > b) return ;
            if(sum) A[++ cnt] = sum;
            DFS(sum*10 + 6);
            DFS(sum*10 + 8);
    }
    
    void DFS_2(int k, bool opt, ll sum){
            if(k == cnt+1){
                    if(sum == 1) return ;
                    if(opt & 1) Tmp_1 += b/sum - (a-1)/sum;
                    else Tmp_1 -= b/sum - (a-1)/sum;
                    return ;
            }
            if(sum > b) return ;
            DFS_2(k+1, opt, sum);
            sum = sum/Gcd(sum, B[k]);
            if((double)sum*B[k] <= b) DFS_2(k+1, opt^1, sum*B[k]);
    }
    
    int main(){
            scanf("%lld%lld", &a, &b);
            DFS(0);
            std::sort(A+1, A+cnt+1);
            for(reg int i = 1; i <= cnt; i ++){
                    if(Used[i]) continue ;
                    for(reg int j = i+1; j <= cnt; j ++)
                            if(A[j]%A[i] == 0) Used[j] = 1;
            }
            int tmp = 0;
            for(reg int i = cnt; i >= 1; i --){
                    if(Used[i]) continue ;
                    if(A[i] > b/3) Tmp_1 += b/A[i] - (a-1)/A[i];
                    else B[++ tmp] = A[i];
            }
            cnt = tmp;
            DFS_2(1, 0, 1);
            printf("%lld
    ", Tmp_1);
            return 0;
    }
    
    
    
  • 相关阅读:
    正则表达式积累
    Windows界面编程第七篇 文件拖拽(文件拖放)
    设置 Eclipse 智能代码提示,大幅度减少 alt+/ 使用频率,打每个字都出现代码提示的办法
    CodeFx:一站式微软开发技术解决方案 .
    JS添加可信站点、修改ActiveX安全设置,禁用弹出窗口阻止程序的方法
    Eclipse支持HTML&JS&ExtJS&jQuery代码智能提示
    如何在Web上判断是否已经安装了某个ActiveX控件
    去掉Eclipses的鼠标悬浮提示和增加输代码提示
    在jsp页面下, 让eclipse完全支持HTML/JS/CSS智能提示
    Java集合类ArrayList循环中删除特定元素
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822565.html
Copyright © 2020-2023  润新知