2393: Cirno的完美算数教室
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 322 Solved: 194
[ Submit][ Status][ Discuss]
Description
~Cirno
发现了一种
baka
数,这种数呢
~
只含有
2
和⑨两种数字
~~
现在
Cirno
想知道
~
一个区间中
~~
有多少个数能被
baka
数整除
~
但是
Cirno
这么天才的妖精才不屑去数啦
只能依靠聪明的你咯。
Input
一行正整数
L R
( 1 < L < R < 10^10)
Output
一个正整数,代表所求的答案
Sample Input
1 100
Sample Output
58
容斥原理+搜索剪枝
首先我们可以预处理出1-r之间所有只由2和9构成的数,最多有2046个。如果一个数是另一个的倍数,显然是没有保留必要的,n^2检查,然后去掉其中是其他数的倍数的数。
根据容斥原理,ans=至少被一个数整除的数的数量-至少被两个数整除的数的数量+至少被三个数整除的数的数量……
然后枚举选择哪些数,这是O(2^n)的,显然会超时。
但事实证明,往往选择几个数做lcm后就会超过r的范围,所以加一个剪枝优化就可以大大降低复杂度,然后这道题就可以过了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 3005
using namespace std;
int cnt,tot;
ll l,r,ans,a[maxn],b[maxn];
bool tag[maxn];
void find(ll x)
{
if (x>r) return;
a[++cnt]=x;
find(x*10+2);find(x*10+9);
}
ll gcd(ll a,ll b)
{
return !b?a:gcd(b,a%b);
}
void dfs(int x,int flag,ll t)
{
if (x>cnt)
{
if (t!=1) ans+=(r/t-(l-1)/t)*flag;
return;
}
dfs(x+1,flag,t);
ll tmp=t/gcd(t,a[x])*a[x];
if (tmp>r) return;
dfs(x+1,-flag,tmp);
}
int main()
{
scanf("%lld%lld",&l,&r);
find(2);find(9);
sort(a+1,a+cnt+1);
F(i,1,cnt) F(j,1,i-1) if (a[i]%a[j]==0){tag[i]=1;break;}
for(int i=1;a[i];i++) if (!tag[i]) b[++tot]=a[i];
cnt=tot;
F(i,1,cnt) a[i]=b[i];
dfs(1,-1,1);
printf("%lld\n",ans);
}