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 }