zoukankan      html  css  js  c++  java
  • 【SCOI2010】幸运数字 容斥原理+乱搞

    描述

    在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!

    但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”

    lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。

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

    输入

    输入数据是一行,包括2个数字a和b

    输出

    输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数

    样例输入[复制]
    1 10

    【样例输入2】
    1234 4321
    【样例输出2】
    809
    样例输出[复制]
    2
    提示

    【数据范围】 对于30%的数据,保证1<=a<=b<=1000000 对于100%的数据,保证1<=a<=b<=10000000000

    标签
    scoi2010省选
     
     
     
     
    还好看出来了这是一道容斥原理的题目,但是有一个问题是我们要容斥哪些元素
    对于10^10来说有10位,所以我们实际上就在十个数字上面选择是6还是8,这些数字都是幸运数字(不是近似的)
    所以实际上所有的幸运数字应该有2047个
    但是我们在筛选的时候肯定不能直接容斥原理因为复杂度可以达到22047,直接爆了啊
    我们发现如果一个幸运数字是另一个的倍数那么这个幸运数字实际上是没有意义的
    所以我们就预处理一下,处理真幸运数字的时候我们就直接枚举每一位上面的可能性就可以了
    去重也很简单,复杂度可以忽略不计
    接着就是搜索
    我们发现在验证是否是大于右边界的时候会爆long long,所以我们要用double来进行验证
    最重要的是乘法的时候要尽量分开写才能保证double比较的时候准确
    总结了一下实际上我们的容斥原理的代码无非就是这样:
     1 void dfs(int dep,int use,int now){
     2     if(dep>总值){
     3         if(!use)return;
     4         if(use%2)ans+;
     5         else ans- ;
     6         return ;
     7     } 
     8     //不选这个数字 
     9     dfs(dep+1,use,now);
    10     //选择这个数字
    11     if(...<边界)
    12     dfs(dep+1,use+1,now*k)    
    13 } 

    那么这道题实际上就是一个道理

    code:
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #define ll long long
     5 #include<cmath>
     6 #include<ctime>
     7 using namespace std;
     8 ll a,b,ans;
     9 long long tot,tot_,p[5000],check[5000],num[5000];
    10 inline ll gcd(ll a,ll b){
    11     if(a<b)swap(a,b);
    12     while(a=a%b)swap(a,b);
    13     return b;
    14 }
    15 inline ll lcm(ll a,ll b){
    16     return a/gcd(a,b)*b;
    17 }
    18 inline void pre(long long dep,long long cnt,long long now,long long x){
    19     if(dep>cnt){//预处理的过程 
    20         p[++tot_]=now;
    21         return;
    22     }
    23     pre(dep+1,cnt,now+x*8,x*10);
    24     pre(dep+1,cnt,now+x*6,x*10);
    25 }
    26 inline void uniq(){//去重的过程,就像自带的函数unique一样 
    27     for(long long i=1;i<=tot_;i++){
    28         for(long long j=1;j<i;j++){
    29             if(p[i]%p[j]==0){
    30                 check[i]=1;
    31                 break;
    32             }
    33         } 
    34     }
    35 }
    36 inline void dfs(ll pos,ll k,ll x){//还是容斥原理 ,传进去的几个参数代表当前的位置,是几个数的lcm,现在的数字是多大 
    37     if(x>b)return;//如果当前数字比右边界还要大就直接剪枝 
    38     if(pos>tot){//如果搜到了大于tot的地方 ,也就是我们枚举完了所有数字 
    39         if(!k)return;//一个数字都没有就是0,肯定是没有什么意义的 (与上一道一样会让答案不准确) 
    40         if(k&1)ans+=b/x-a/x;//如果是奇数个数字组成的数字那么就要加上去 
    41         else ans-=b/x-a/x;//如果是偶数个就要减掉 
    42         return;
    43     }
    44     dfs(pos+1,k,x);//如果这一位不搜索 
    45     ll tmp=x/gcd(x,num[pos]);//这一步是非常非常重要的,这可以决定你是否tle,注意在大数运算的时候要尽量分开进行运算,这样后面的double运算才准确 
    46     if((double)1.0*tmp*num[pos]>(double)b)return;//如果他们的最小的公倍数都超过了右边界,就要直接剪枝掉 
    47     dfs(pos+1,k+1,lcm(x,num[pos]));//乘上这个数字直接往下走 
    48 }
    49 int main(){
    50     cin>>a>>b;
    51     for(long long i=1;i<=10;i++)pre(1,i,0,1);
    52     uniq();
    53     for(long long i=tot_;i>=1;i--)if(!check[i])num[++tot]=p[i];
    54     a--;
    55     dfs(1,0,1);
    56     cout<<ans;
    57     return 0;
    58 }
  • 相关阅读:
    抽象工厂例子
    学习boost::asio一些小例子
    boost::asio学习(定时器)
    共享内存
    网络流程图
    粘包
    端游服务器群
    38 写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度。
    37 有n个人围成一圈,顺序排号,从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号那位.
    36 有n个整数,使其前面各数顺序向后移n个位置,最后m个数变成最前面的m个数
  • 原文地址:https://www.cnblogs.com/saionjisekai/p/9711388.html
Copyright © 2011-2022 走看看