这道题首先需要注意的是数据范围,两个10^10规模的数相乘会爆long long,所以要尽量避免乘法。
这道题很容易让人想到数位DP,不过这个想法是错误的,这就提示在考试中如果想到一种方法,但是却怎么也想不出来时不要盲目地陷入其中,而要冷静思考有没有其他的解决办法。
这个解决办法就是利用容斥原理。
先dfs出所有的“真正的”幸运数字,然后再利用容斥原理找出近似幸运数字。
为了避免超时,需要一个剪枝,就是如果一个幸运数字是一个比他小的幸运数字的倍数,则这个幸运数字不用考虑,因为后面会统计到。
把处理过的数字从大到小排序,这样可以避免TLE,因为从大的数字开始更有可能大于limit从而跳出dfs。
根据容斥原理,答案就为:每个数的倍数个数-每两个数的公倍数个数+每三个数...这个可以通过传递数的个数来判断。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 3000 + 10; bool flag[maxn]; long long luck[maxn]; long long cnt = 0,ret = 0; long long limit,a,b; void init() { freopen("bzoj1853.in","r",stdin); freopen("bzoj1853.out","w",stdout); } bool cmp(const long long &a,const long long &b) { return a > b; } void dfs(long long num) { if(num > limit)return; if(num != 0)luck[cnt++] = num; dfs(num * 10 + 6); dfs(num * 10 + 8); } void readdata() { scanf("%lld%lld",&a,&b); } long long gcd(long long a,long long b) { long long t; while(b != 0) { t = a % b; a = b; b = t; } return a; } void work(int step,long long lcm,int tot) { if(step == cnt) { if(tot) { long long tmp = limit / lcm; if(tot&1)ret += tmp; else ret -= tmp; } return; } for(int i = step;i < cnt;i++) { long long tmp = gcd(lcm,luck[step]); if((double)lcm / tmp > (double)limit / luck[step])return; tmp = lcm * luck[step] / tmp; work(i + 1,tmp,tot + 1); } } long long calc(long long x) { if(x < 6)return 0; ret = 0,cnt = 0; limit = x; dfs(0); stable_sort(luck,luck + cnt); for(int i = 0;i < cnt;i++) { flag[i] = true; for(int j = 0;j < i;j++) { if(luck[i] % luck[j] == 0)flag[i] = false; } } int tmp = cnt; cnt = 0; for(int i = 0;i < tmp;i++) { if(flag[i])luck[cnt++] = luck[i]; } stable_sort(luck,luck + cnt,cmp); for(int i = 0;i < cnt;i++) { work(i,1,0); } return ret; } void solve() { long long ans = calc(b) - calc(a - 1); printf("%lld",ans); } int main() { init(); readdata(); solve(); return 0; }