BZOJ1853 [Scoi2010]幸运数字 容斥原理
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1853
题意概括
求一个区间范围内,近似幸运数字的个数。
定义:
幸运数字:仅由6或者8组成的数字。
近似幸运数字:幸运数字的正整数倍。
题解
我们发现幸运数字很少。
然后,我们考虑容斥。
我们发现原来的大整数除几次机会很小。所以记忆化dfs容斥,中途跳出。
这样可以节省很多时间。
然后居然过去了。
代码
#include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> using namespace std; typedef unsigned long long LL; int totluck; LL L,R,ans,luck[2050]; bool alive[2050]; void df(LL x){ if (x>R) return; if (x>0) luck[++totluck]=x; df(x*10+6); df(x*10+8); } LL gcd(LL a,LL b){ return b?gcd(b,a%b):a; } void dfs(int num,int pos,LL Lcm){ if (pos>totluck){ if (num==0) return; if (num%2==1) ans+=R/Lcm-(L-1)/Lcm; else ans-=R/Lcm-(L-1)/Lcm; return; } dfs(num,pos+1,Lcm); LL g=Lcm/gcd(Lcm,luck[pos]); if (1.0*g*luck[pos]<=R) dfs(num+1,pos+1,g*luck[pos]); } bool cmp(LL a,LL b){ return a>b; } int main(){ scanf("%llu%llu",&L,&R); totluck=0; df(0); sort(luck+1,luck+totluck+1,cmp); memset(alive,true,sizeof alive); for (int i=1;i<totluck;i++) for (int j=i+1;j<=totluck&&alive[i];j++) if (luck[i]%luck[j]==0) alive[i]=0; int to=0; for (int i=1;i<=totluck;i++) if (alive[i]) luck[++to]=luck[i]; totluck=to; ans=0; dfs(0,1,1); printf("%llu\n",ans); return 0; }