P2567 [SCOI2010]幸运数字

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2567 [SCOI2010]幸运数字相关的知识,希望对你有一定的参考价值。

P2567 [SCOI2010]幸运数字

题意:

我们规定只含6或8的数字为幸运号码,而幸运号码的倍数我们也认为是幸运号码,问[l,r]中有多少个幸运号码?

题解:

第一反应是数位dp,但其实不是,我们可以打表观察下,幸运数字其实很少,也就1000多个,所有我门全可以用容斥来去做。
先去搞定所有范围内的合法数字,然后直接容斥
对于有个幸运数字X,在[L,R]中X的倍数的个数,就是R/X-(L-1)/X,但是两个幸运数字有可能会存在交集,即重复计算,所有要用到容斥,选一个幸运数字-选两个幸运数字的lcm+选3个幸运数字的lcm-…(容斥原理)
但是直接这样做也会超时,需要剪枝:

  1. 对于两个幸运数字a,b,如果b%a==0,那么如果b/x就一定包含了a/x,因此这样的b没有必要,也就是幸运数字的倍数我们就不用管了,这样就剩下943个幸运号码
  2. 如果当然的lcm大于r,就没必要继续搜索了,因为已经超出范围
  3. 我们将幸运数字从大到小排序,可以使得lcm更快的超越上届r

代码:

#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\\n",a,b);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll=1e18;
const int INF_int=0x3f3f3f3f;
inline ll read(){
   ll s=0,w=1ll;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}
void rd_test(){
	#ifdef ONLINE_JUDGE
	#else
		startTime = clock(); //计时开始
        freopen("2567.in","r",stdin);
	#endif
}
void Time_test(){
	#ifdef ONLINE_JUDGE
	#else
		endTime = clock(); //计时结束
   		printf("\\n运行时间为:%lfs\\n",(double)(endTime - startTime) / CLOCKS_PER_SEC);
	#endif
}
const int N=20005;
const int dig1=6;
const int dig2=8;
ll l,r,a[N],ans,tot;
inline bool cmp(ll x,ll y){return x>y;}
void init(ll x){
    if(x>r)return;
    if(x>0)a[++tot]=x;
    init(x*10+dig1);
    init(x*10+dig2);
}
ll gcd(ll x,ll y){
    return y?gcd(y,x%y):x;
}
void dfs(int now,int sum,ll k){
    if(now>tot){
        if(!sum)return;
        if(sum&1)ans+=(r/k-(l-1)/k);
        else ans-=(r/k-(l-1)/k);
        return;
    }
    dfs(now+1,sum,k);
    if((double long)k/gcd(k,a[now])<=(double long)r/a[now])
        dfs(now+1,sum+1,k*a[now]/gcd(k,a[now]));
}
int main(){
    scanf("%lld%lld",&l,&r);
    init(0);
	sort(a+1,a+tot+1,cmp);
    int tmp=0;
    for(int i=1;i<=tot;i++){
        for(int j=i+1;j<=tot&&a[i];j++){
            if(!a[j])continue;
            if(a[i]%a[j]==0)a[i]=0;
        }
        if(a[i])a[++tmp]=a[i];
    }
    tot=tmp;
    dfs(1,0,1);
    printf("%lld\\n",ans);
    return 0;
}


以上是关于P2567 [SCOI2010]幸运数字的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1853: [Scoi2010]幸运数字

1853[Scoi2010]幸运数字 容斥

bzoj1853SCOI2010幸运数字

[SCOI2010]幸运数字

[SCOI2010]幸运数字

「SCOI2010」幸运数字