首页 > 代码库 > 【bzoj1853】 Scoi2010—幸运数字
【bzoj1853】 Scoi2010—幸运数字
http://www.lydsy.com/JudgeOnline/problem.php?id=1853 (题目链接)
今天考试考了容斥,结果空知道结论却不会写= =
题意:求区间中不含6,8两个数字及由6,8组成的数字的倍数的的数有几个
Solution
容斥原理。
先把所有的幸运数字都蒯到一个数组里,将两两之间可以整除的数只留下一个小的。
接下来如果暴力组合统计答案的话肯定会TLE,因为就算去掉了可以被整除的数以后还是有1000多个幸运数组。我们考虑dfs,x记录当前已经枚举到了第几个数,y记录已经选了几个数,z表示这几个数的最小公倍数。从大往小枚举,然后加个剪枝,这个神奇的剪枝就直接把复杂度大大降低了,其实就是当最小公倍数大于上界r时返回= =。
为什么会这样呢,我想了下。只有当前位数T比较大的情况下,T位幸运数字才会比较多,而当T比较大的情况下,两个幸运数字的lcm(最小公倍数)就会很大,很有可能超越上界,所以这个剪枝是很有效的。
代码:
// bzoj1853#include<algorithm>#include<iostream>#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<map>#define inf 2147483640#define LL long long#define free(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);using namespace std;inline LL getint() { LL x=0,f=1;char ch=getchar(); while (ch>‘9‘ || ch<‘0‘) {if (ch==‘-‘) f=-1;ch=getchar();} while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘;ch=getchar();} return x*f;}int n,m,vis[100010];LL l,r,ans,a[100010],b[100010];void pre(int x,LL y) { if (y>r)return; if (x>0) a[++m]=y; pre(x+1,y*10+6); pre(x+1,y*10+8);}LL gcd(LL x,LL y) { return x%y==0?y:gcd(y,x%y);}void dfs(int x,int y,LL z) { if (x>n) { if (y&1) ans+=r/z-(l-1)/z; else if (y) ans-=r/z-(l-1)/z; return; } dfs(x+1,y,z); LL tmp=z/gcd(a[x],z); if ((double)a[x]*tmp<=r) dfs(x+1,y+1,a[x]*tmp);}int main() { scanf("%lld%lld",&l,&r); pre(0,0); sort(a+1,a+1+m); memset(vis,0,sizeof(vis)); for (int i=1;i<=m;i++) if (!vis[i]) { for (int j=i+1;j<=m;j++) if (a[j]%a[i]==0) vis[j]=1; b[++n]=a[i]; } for (int i=1;i<=n;i++) a[n-i+1]=b[i]; dfs(1,0,1); printf("%lld",ans); return 0;}
【bzoj1853】 Scoi2010—幸运数字
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。