[BZOJ2301][HAOI2011]Problem B
题目大意
对于给出的 次询问,每次给定 五个数,询问 的值。数据保证 。
解析
首先利用容斥原理将一个询问拆分成四个,每次求 的值。考虑莫比乌斯反演,设 为 的对数, 为 的个数,则有以下关系:
只需快速求出 。若 ,则 ,只需统计有多少对 。得:
由莫比乌斯反演可求得 :
暴力求 是 ,注意到 有至多 个取值,所以可以考虑使用数论分块求解
证明:
- 当 时,由于 只有 个,所以 也至多有 个取值。
- 当 时,由于 只有 个,所以 也只有至多 个取值。
- 故 至多有 个取值。
同理 也只有至多 个取值,所以 和 都不变的段数有 段。对于相等的段,求出 函数的前缀和,即可批量计算这一个段的答案。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 50010;
int T, tot, prime[maxn], mu[maxn], sum[maxn];
void sieve() {
fill(prime, prime + maxn, 1);
mu[1] = 1, tot = 0;
for (int i = 2; i < maxn; i++) {
if (prime[i]) {
prime[++tot] = i, mu[i] = -1;
}
for (int j = 1; j <= tot && i * prime[j] < maxn; j++) {
prime[i * prime[j]] = 0;
if (i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
} else {
mu[i * prime[j]] = -mu[i];
}
}
}
for (int i = 1; i < maxn; i++) {
sum[i] = sum[i - 1] + mu[i];
}
}
int calc(int n, int m, int k) {
if (n > m) swap(n, m);
int ans = 0;
n /= k, m /= k;
for (int i = 1, nxt = 1; i <= n; i = nxt + 1) {
nxt = min(n / (n / i), m / (m / i));
ans += (sum[nxt] - sum[i - 1]) * (n / i) * (m / i);
}
return ans;
}
int main() {
sieve();
scanf("%d", &T);
while (T--) {
int a, b, c, d, k;
scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
printf("%d\n", calc(b, d, k) - calc(b, c - 1, k) - calc(a - 1, d, k) + calc(a - 1, c - 1, k));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具