洛谷 P2567 [SCOI2010]幸运数字 题解
主要思路
显然我们需要求出所有范围内的合法幸运号码,然后容斥。
容斥就是暴搜+剪枝。
剪枝&优化
- 一个幸运号码是另一个幸运号码的倍数,则这个数不会产生任何贡献,剪枝。
- 选定数的 LCM 大于 则直接退出。
- 将幸运号码从大到小排序,这样搜索的时候能更快突破上界。
- 对于所有大于 的幸运号码,不能与其他任何数字合并,于是我们直接预先算出这些数字,对剩余部分暴搜。
代码
//P2567 [SCOI2010]幸运数字 #include <bits/stdc++.h> using namespace std; #define ll long long const int MOD=1e9+7; int cnt; ll a[5050],res,l,r; bool vis[5050]; ll math_GCD(ll x,ll y) { return !y?x:math_GCD(y,x%y); } void _DFS(ll x)//不开 long long 就会 RE { if(x>r) { return; } if(x) { a[++cnt]=x; } _DFS(x*10+6); _DFS(x*10+8); return; } bool _check(ll a,ll b)//大于则直接剪枝 { ll x=a/r,y=b/r; if(x*y) { return true; } return x*y>r; } void _calc(int x,ll sum,int count) { ll d;//不开 long long 就会 RE if(sum>r)//超过上界则直接返回 { return; } if(x>cnt) { if(sum!=1) { if(count&1) { res+=r/sum-l/sum; } else { res-=r/sum-l/sum; } } return; } _calc(x+1,sum,count); d=a[x]/math_GCD(sum,a[x]); if(!_check(sum,d)) { _calc(x+1,sum*d,count+1); } return; } signed main() { int temp=0; scanf("%lld%lld",&l,&r); l--; _DFS(0); sort(&a[1],&a[cnt+1]); for(int i=1;i<=cnt;i++) { for(int j=1;j<i;j++) { if(a[i]%a[j]==0)//若为倍数,没有贡献,剪枝 { vis[i]=true; break; } } } for(int i=1;i<=cnt;i++) { if(!vis[i]) { if(a[i]<=r/3) { a[++temp]=a[i]; } else { res+=r/a[i]-l/a[i]; } } } cnt=temp; reverse(&a[1],&a[cnt+1]); _calc(1,1,0); printf("%lld\n",res); return 0; } /* * 洛谷 * https://www.luogu.com.cn/problem/P2567 * C++20 -O0 * 2020.10.1 */
本文作者:Day_Dreamer_D
本文链接:https://www.cnblogs.com/2020gyk080/p/16747595.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步