[SCOI2010]幸运数字
题目描述
在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。
现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。
输入输出格式
输入格式:
输入数据是一行,包括2个数字a和b
输出格式:
输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数
输入输出样例
1 10
2
说明
对于30%的数据,保证1<=a<=b<=1000000
对于100%的数据,保证1<=a<=b<=10000000000
题解:
容斥原理+dfs
存在左界不难,只要容斥时减去左边的伪幸运数,假设要求[a,b],其中一个因数为p1
只要由b/p1->b/p1-(a-1)/p1
首先dfs求出所有幸运数字,
总方案ans=(b/p1-(a-1)/p)+(b/p2-(a-1)/p)+...-(b/p1p2-(a-1)/p1p2)-......
不过直接dfs显然超时
优化:
1.减小搜索范围,根据容斥原理,可以知道当px|py时,py与px显然重合且py无用,直接去掉,形成新的幸运数组
2.时刻判断lcm(s,x)是否超过r,超过则不选该数x,记住判断时由于数太大,可能会溢出,所以要把判断换一下
s*x/gcd(s,x)<=r ->s/gcd(s,x)<=(double)r/x这里的double是为防止整形判断有误(应该不会错,但会慢一点)
3.完成以上2步应该是60分,还有关键一点:调整搜索顺序
显然先从大的幸运数开始容斥,会在开始时产生较少的分支,而搜索算法的搜索树靠近根的分支越少就越快
所以将幸运数重新排序就可以AC了,比原来快的多
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 lol luck[3001],l,r,ans; 9 int cnt; 10 bool cmp(lol a,lol b) 11 { 12 return a>b; 13 } 14 void dfs_pre(lol s) 15 { 16 if (s>r) return; 17 if (s) 18 { 19 cnt++; 20 luck[cnt]=s; 21 } 22 dfs_pre(s*10+6); 23 dfs_pre(s*10+8); 24 } 25 lol gcd(lol a,lol b) 26 { 27 if (!b) return a; 28 return gcd(b,a%b); 29 } 30 void dfs(int x,lol s,int f) 31 { 32 if (x==0) 33 { 34 ans+=f*(r/s-(l-1)/s); 35 return; 36 } 37 lol g=gcd(s,luck[x]); 38 if ((double)s/(double)g<=(double)r/(double)luck[x]) dfs(x-1,s/g*luck[x],f*(-1)); 39 dfs(x-1,s,f); 40 } 41 int main() 42 {int i,j; 43 cin>>l>>r; 44 dfs_pre(0); 45 for (i=1;i<=cnt;i++) 46 { 47 for (j=1;j<i;j++) 48 if (luck[j]!=-1&&luck[i]%luck[j]==0) 49 luck[i]=-1; 50 } 51 sort(luck+1,luck+cnt+1,cmp); 52 for (i=cnt;i>=1;i--) 53 if (luck[i]==-1) cnt--; 54 sort(luck+1,luck+cnt+1); 55 dfs(cnt,1,-1); 56 cout<<r-l+1+ans; 57 }