zoukankan      html  css  js  c++  java
  • BZOJ 1853 【SCOI2010】 幸运数字

    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]内,“近似幸运号码”的个数。

    Input

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

    Output

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

    HINT

    【数据范围】
    对于$30\%$的数据,保证$1 leqslant a leqslant b leqslant1000000$
    对于$100\%$的数据,保证$1 leqslant a leqslant b leqslant 10000000000$
     
      这道题一开始我还以为需要用到什么神奇的数学推导,或者一些什么奇妙的数学公式,然后看了题解之后发现是一道搜索题……
      一个非常显然的事实就是幸运号码不会太多。把表打出来,就会发现在$10^{10}$以内的幸运数只有$2000$多一点……
      这个时候一个非常显然的想法就是对这些数进行容斥,即加上每个数的倍数个数,减去两个数的倍数个数,加上三个的,……以此类推。
      这样的复杂度显然是不对的,理论上可达$O(2^x)$,其中$x$为幸运数个数。但是由于多个数的倍数不能超过右边界,就可以减一大刀,实际复杂度低了不知道多少。
      但是这样任然不够。我们还可以对幸运数进行处理,将其中是另外的幸运数的倍数的数给去掉。这样可以将需要考虑的数的个数减掉一半左右。
      最后还有一个小优化,那就是将最后需要处理的幸运数按从大到小排好序。这样可以让乘积尽早变得更大,可以减掉许多不必要的计算。
      加了上述优化,就差不多可以$AC$了。
      下面贴代码:
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    #define maxn 10010
    
    using namespace std;
    typedef long long llg;
    
    int la,lb,ci;
    llg now,ans,mi[15],l,r;
    llg a[maxn],b[maxn];
    
    void search(int d){
    	if(now>r) return;
    	llg xx=now;
    	now=xx+6*mi[d]; search(d+1);
    	now=xx+8*mi[d]; search(d+1);
    	if(xx) a[++la]=xx; now=xx;
    }
    
    llg gcd(llg a,llg b){
    	llg r=a%b;
    	while(r) a=b,b=r,r=a%b;
    	return b;
    }
    
    void dfs(int j){
    	if(j==lb+1){
    		if(!ci) return;
    		if(ci&1) ans+=r/now-(l-1)/now;
    		else ans-=r/now-(l-1)/now;
    		return;
    	}
    	dfs(j+1); llg xx=now; ci++;
    	now=xx/gcd(xx,b[j]);
    	if((double)now*b[j]<=r){
    		now*=b[j];
    		if(now<=r) dfs(j+1);
    	}
    	ci--; now=xx;
    }
    
    int main(){
    	File("a");
    	mi[0]=1;
    	for(int i=1;i<=10;i++) mi[i]=mi[i-1]*10;
    	scanf("%lld %lld",&l,&r);
    	search(0); sort(a+1,a+la+1);
    	for(int i=1;i<=la;i++){
    		b[++lb]=a[i];
    		for(int j=1;j<lb;j++)
    			if(a[i]%b[j]==0){lb--; break;}
    	}
    	for(int i=1;i<=lb/2;i++) swap(b[i],b[lb-i+1]);
    	now=1; dfs(1);
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    RedHat 7 安装PostgreSQL 10.5
    百万级数据库优化方案
    所有文章的测试Demo
    PostGreSql安装
    windows server 2016部署服务
    Spring MVC Hello World 404
    Unity攻略
    Unity判断用户联网状态,WiFi/移动网络/无网络
    Unity UGUI Layout自动排版组件用法介绍
    Unity中对系统类进行扩展的方法
  • 原文地址:https://www.cnblogs.com/lcf-2000/p/6217894.html
Copyright © 2011-2022 走看看