[BZOJ 1853][SCOI 2010]幸运数字(容斥原理+DFS)
2015-02-26 10:22
316 查看
题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1853思路
我们可以先求出[1,r][1,r]范围内形如6…68…86…6这样符合题意的数的升序序列AA,然后对于任意的i<j,Aj=kAii,删去AjA_j,那么我们就能得到一个新的升序数列BB,在这个序列中,没有一个数是另一个数的倍数。然后很显然就是容斥了,最终的答案=是1个数的倍数的数-是2个数lcm的数+是3个数lcm的数-是4个数lcm的数……
这个可以用DFS来做,DFS时记录当前的lcm是多少,以及是多少个数的lcm,最终枚举完BB序列中所有数是否被计入的情况后,是偶数个数(注意特判0)的lcm就减,是奇数个数的lcm就加,因为在区间[1,x][1,x]中,是lcmlcm的倍数(包含lcmlcm)的数的个数为[xlcm][\frac{x}{lcm}]个,那么当前的在区间[l,r][l,r]中是xxx个数的lcmlcm的数的个数就是[rlcm]−[l−1lcm][\frac{r}{lcm}]-[\frac{l-1}{lcm}]
然后有个剪枝,如果当前的lcm已经超出了rr的范围,很显然它是不在题目给的范围内的,那么就不继续搜了,直接退出搜索。由于如果超出rr的范围的话,lcm的规模大概在101810^18级别之上,用long long int会爆掉数据范围,只能用double先估算一下lcmlcm的规模,然后剪枝,或者直接用unsigned long long int(比前面的方法快100ms左右,但是个人认为不推荐在实际考试时用,因为依然有可能被某些数据爆掉,很危险)
但是尽管这样做仍然会TLE,因为如果从小往大枚举BB序列中的数是否被计入lcm的话,剪枝时间比较晚,优化效果不好,而题目时限为2s,因此要从大往小枚举BB序列,在同样的序列下,从大往小枚举会尽早剪枝,效率很高,极限数据下大概能比优化前的程序快一两秒
代码
1、用double先估算lcm规模/************************************************************** Problem: 1853 User: qpswwww Language: C++ Result: Accepted Time:736 ms Memory:1004 kb ****************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 10050 using namespace std; typedef long long int LL; LL l,r,ans,a[MAXN],b[MAXN]; //a数组中保存那些是2..29...92..2类型的数,b数组中是去掉了倍数的a数组 bool mark[MAXN]; //mark[i]=true表明a[i]是某些a[j]的倍数,j<i LL gcd(LL a,LL b) { if(b==0) return a; return gcd(b,a%b); } void DFS1(LL x) //当前找到的数字是x { if(x>r) return; a[++a[0]]=x; DFS1(x*10+6); DFS1(x*10+8); } void DFS2(int pos,LL used,LL nowlcm) //当前DFS到了b[pos],used=已经用了的数字个数,nowlcm是之前用了的数字的lcm { if(pos>b[0]) { if(used&1) ans+=r/nowlcm-(l-1)/nowlcm; //加上是奇数个数公倍数的数量 else if(used) ans-=r/nowlcm-(l-1)/nowlcm; //减去是偶数个数的公倍数的数量 return; } DFS2(pos+1,used,nowlcm); LL newlcm=nowlcm/gcd(nowlcm,b[pos]); if((double)newlcm*b[pos]>r) return; //剪枝:前pos个数的最小公约数已经超过了r,就不继续DFS了 DFS2(pos+1,used+1,newlcm*b[pos]); } int main() { scanf("%lld%lld",&l,&r); DFS1(6); DFS1(8); sort(a+1,a+a[0]+1); for(int i=1;i<=a[0];i++) if(!mark[i]) { b[++b[0]]=a[i]; for(int j=i+1;j<=a[0];j++) if(!(a[j]%a[i])) //a[j]是a[i]的倍数 mark[j]=1; } for(int i=1;i<=b[0];i++) a[b[0]-i+1]=b[i]; for(int i=1;i<=b[0];i++) b[i]=a[i]; DFS2(1,0,1); printf("%lld\n",ans); return 0; }
2、直接使用unsigned long long int
/************************************************************** Problem: 1853 User: qpswwww Language: C++ Result: Accepted Time:632 ms Memory:1004 kb ****************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 10050 using namespace std; typedef unsigned long long int LL; LL l,r,ans,a[MAXN],b[MAXN]; //a数组中保存那些是2..29...92..2类型的数,b数组中是去掉了倍数的a数组 bool mark[MAXN]; //mark[i]=true表明a[i]是某些a[j]的倍数,j<i LL gcd(LL a,LL b) { if(b==0) return a; return gcd(b,a%b); } void DFS1(LL x) //当前找到的数字是x { if(x>r) return; a[++a[0]]=x; DFS1(x*10+6); DFS1(x*10+8); } void DFS2(int pos,LL used,LL nowlcm) //当前DFS到了b[pos],used=已经用了的数字个数,nowlcm是之前用了的数字的lcm { if(pos>(int)b[0]) { if(used&1) ans+=r/nowlcm-(l-1)/nowlcm; //加上是奇数个数公倍数的数量 else if(used) ans-=r/nowlcm-(l-1)/nowlcm; //减去是偶数个数的公倍数的数量 return; } DFS2(pos+1,used,nowlcm); LL newlcm=(nowlcm*b[pos])/gcd(nowlcm,b[pos]); if(newlcm>r) return; //剪枝:前pos个数的最小公约数已经超过了r,就不继续DFS了 DFS2(pos+1,used+1,newlcm); } int main() { scanf("%llu%llu",&l,&r); DFS1(6); DFS1(8); sort(a+1,a+a[0]+1); for(int i=1;i<=(int)a[0];i++) for(int j=i+1;j<=(int)a[0];j++) if(a[j]%a[i]==0) mark[j]=true; for(int i=1;i<=(int)a[0];i++) if(!mark[i]) b[++b[0]]=a[i]; for(int i=1;i<=b[0];i++) a[b[0]-i+1]=b[i]; //一个优化 for(int i=1;i<=b[0];i++) b[i]=a[i]; DFS2(1,0,1); printf("%llu\n",ans); return 0; }
相关文章推荐
- BZOJ 1853 SCOI2010 幸运数字 容斥原理+DFS
- BZOJ 1853: [Scoi2010]幸运数字
- bzoj 1853: [Scoi2010]幸运数字(容斥原理)
- AC日记——[SCOI2010]幸运数字 bzoj 1853
- bzoj 1853: [Scoi2010]幸运数字 容斥
- 【BZOJ 1853】【SCOI 2010】幸运数字
- 【bzoj 1853】 [Scoi2010] 幸运数字
- BZOJ 1853 【SCOI2010】 幸运数字
- BZOJ2393 & 1853 [Scoi2010]幸运数字 【搜索 + 容斥】
- Bzoj 1853: [Scoi2010]幸运数字 容斥原理,深搜
- bzoj 1853: [Scoi2010]幸运数字&&2393: Cirno的完美算数教室【容斥原理】
- BZOJ_2393_Cirno的完美算数教室&&BZOJ_1853_[Scoi2010]幸运数字 _深搜+容斥原理
- [BZOJ]1853: [Scoi2010]幸运数字 容斥原理
- 【SCOI2010】【BZOJ1853】幸运数字
- BZOJ 1853: [Scoi2010]幸运数字(容斥原理)
- BZOJ 1853 [Scoi2010]幸运数字 (容斥原理)
- 【BZOJ 1853】[Scoi2010]幸运数字 【容斥原理】
- ●BZOJ 1853 [Scoi2010]幸运数字
- BZOJ 1853 [Scoi2010]幸运数字
- 【BZOJ 1853】 1853: [Scoi2010]幸运数字 (容斥原理)