zoukankan      html  css  js  c++  java
  • bzoj1853: [Scoi2010]幸运数字 dp+容斥原理

    在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。 现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。Input输入数据是一行,包括2个数字a和bOutput输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数Sample Input

    【样例输入1】
    1 10
    【样例输入2】
    1234 4321

    Sample Output

    【样例输出1】
    2
    【样例输出2】
    809

    Hint

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

    显然容斥,剩下的就是黑科技的问题了。 
    容斥的话,就是我们首先预处理出来所有的幸运数字,然后筛一遍,筛掉所有是其他幸运数字倍数的数避免重复计算。 
    然后就是选1个-选2个的lcm+选3个的lcm-…. 
    一个dfs搞定即可。 
    但是需要注意,dfs内部的lcm我们不可以求出来,因为会爆long long ,是的我也不知道为什么。 
    所以我们需要转成double 型比较是否越界。 
    另外,我们筛完之后的那些数字最好按照从大到小的顺序排,如果从小到大的话常数大一点点,但就是这么一点点你就过不去,貌似是从大到小的话更能够对一些非法状态早排除吧

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm> 
     6 #define ll long long 
     7 #define N 10001
     8 using namespace std;
     9 
    10 ll l,r;
    11 int t,n,m;
    12 ll ans;
    13 ll a[N],b[N];
    14 bool vis[N];
    15 
    16 ll gcd(ll a,ll b)
    17 {
    18     return b?gcd(b,a%b):a;
    19 }
    20 void pre(int x,ll y)
    21 {
    22     if(y>r)return;
    23     if(x>0)a[++m]=y;
    24     pre(x+1,y*10+6);
    25     pre(x+1,y*10+8);
    26 }
    27 void dfs(int x,int y,ll z)
    28 {
    29     if(x>n)
    30     {
    31         if(y&1)ans+=r/z-(l-1)/z;
    32         else if(y)ans-=r/z-(l-1)/z;
    33         return;
    34     }
    35     dfs(x+1,y,z);
    36     ll tmp=z/gcd(a[x],z);
    37     if(((double)a[x]*tmp)<=r) dfs(x+1,y+1,a[x]*tmp);
    38 }
    39 int main()
    40 {
    41     scanf("%lld%lld",&l,&r);
    42     pre(0,0);
    43     sort(a+1,a+m+1);
    44     for(int i=1;i<=m;i++)
    45        if(!vis[i])
    46        {
    47               b[++n]=a[i];
    48               for(int j=i+1;j<=m;j++)
    49                   if(!(a[j]%a[i]))
    50                       vis[j]=1;
    51        }
    52     for(int i=1;i<=n;i++)
    53        a[n-i+1]=b[i];
    54     dfs(1,0,1);
    55     printf("%lld",ans);
    56 }
  • 相关阅读:
    如何:使用向导来处理项目模板 【转载】
    .NET : 如何在生成XML文档时绑定样式表
    如何在生产环境部署K2的流程
    SQL Server 数据库文档生成工具开源项目【合作邀请】
    新的纪元
    SharePoint : 谁说WSS不支持审核
    C++ : 类型的别名和对象的别名
    C++: 引用和地址运算符
    Infopath : 如何通过代码复制格式文本框的值
    正确地使用投影仪和移动硬盘
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/7632823.html
Copyright © 2011-2022 走看看