P2567 [SCOI2010]幸运数字
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2567 [SCOI2010]幸运数字相关的知识,希望对你有一定的参考价值。
题意:
我们规定只含6或8的数字为幸运号码,而幸运号码的倍数我们也认为是幸运号码,问[l,r]中有多少个幸运号码?
题解:
第一反应是数位dp,但其实不是,我们可以打表观察下,幸运数字其实很少,也就1000多个,所有我门全可以用容斥来去做。
先去搞定所有范围内的合法数字,然后直接容斥
对于有个幸运数字X,在[L,R]中X的倍数的个数,就是R/X-(L-1)/X,但是两个幸运数字有可能会存在交集,即重复计算,所有要用到容斥,选一个幸运数字-选两个幸运数字的lcm+选3个幸运数字的lcm-…(容斥原理)
但是直接这样做也会超时,需要剪枝:
- 对于两个幸运数字a,b,如果b%a==0,那么如果b/x就一定包含了a/x,因此这样的b没有必要,也就是幸运数字的倍数我们就不用管了,这样就剩下943个幸运号码
- 如果当然的lcm大于r,就没必要继续搜索了,因为已经超出范围
- 我们将幸运数字从大到小排序,可以使得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]幸运数字的主要内容,如果未能解决你的问题,请参考以下文章