[SCOI2010] 幸运数字
前言
看过
思路
首先转化题意, 规定由
容易发现 "幸运数字" 是好算的, 打表或者记忆化搜索都行
考虑怎么计算一个集合中数的倍数个数
联想到之前一道题, 考虑容斥原理去做, 这个题还不太需要筛子
具体怎么容斥原理?
你注意到我们这个题可以用差分的想法去做, 只需要考虑
枚举集合, 可以知道
这样直接做是
怎么优化
考虑
- 发现对于两个幸运号码
, 如果 , 那么对于所有的 就一定有 , 因此这样的 是不必要的, 去掉所有这样的 后, 还剩下 个幸运号码 - 当前的
一旦大于 就不再继续搜索, 这样复杂度就能大大降低 - 将预处理出的幸运号码从大到小排序, 使
能更快地超越上界
实现
考虑容斥怎么写, 观察到可以用搜索的方式方便剪枝
#include <bits/stdc++.h>
#define ull unsigned long long
#define lcm(a, b) a / std::__gcd(a, b) * b
const int N = 1e6 + 5;
ull l, r, ans, lucky[N], luc[N];
int cnt1, len;
bool vis[N];
inline void predfs(ull x) {
if (x > r) return;
lucky[++cnt1] = x;
predfs(x * 10 + 6); predfs(x * 10 + 8);
}
inline void dfs(int x, int c, ull w) {
if (x > len) {
if (c == 0) return;
if (c & 1) ans += r / w - (l - 1) / w;
else ans -= r / w - (l - 1) / w;
return;
}
dfs(x + 1, c, w); // 跳过
if (lcm(w, luc[x]) <= r) dfs(x + 1, c + 1, lcm(w, luc[x])); // 选择
}
signed main()
{
scanf("%lld %lld", &l, &r);
predfs(0);
std::sort(lucky + 1, lucky + cnt1 + 1);
for (int i = 2; i <= cnt1; i++) {
if (vis[i]) continue;
luc[++len] = lucky[i];
for (int j = i + 1; j <= cnt1; j++)
if (lucky[j] % lucky[i] == 0) vis[j] = true;
}
std::reverse(luc + 1, luc + len + 1);
dfs(1, 0, 1);
printf("%lld", ans);
return 0;
}
总结
一类: 先找到一个集合, 再求这个集合中的数的倍数的问题, 使用容斥原理方便解决
很大的算法也可以考虑剪枝
容斥是可以用搜索解决的
后话
讲真这个题最难的部分是优化到
vivo 50
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫