2820: YY的GCD
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 2285 Solved: 1222
[Submit][Status][Discuss]
Description
神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种
傻×必然不会了,于是向你来请教……多组输入
Input
第一行一个整数T 表述数据组数接下来T行,每行两个正整数,表示N, M
Output
T行,每行一个整数表示第i组数据的结果
Sample Input
2
10 10
100 100
10 10
100 100
Sample Output
30
2791
2791
HINT
T = 10000
N, M <= 10000000
分析:显然这道题是一道莫比乌斯反演的题.和bzoj2301很相似,只是这道题如果想要用那道题的方法还要枚举一个质数,再乘上根号的时间直接就爆炸了.
考虑答案式子:,实际上就是枚举了一个质数p和p的倍数d来计算答案.考虑怎么求它.如果用bzoj2301的方法来求,那么μ函数中的式子要是一个分式才行,那么设T=pd,d=T/p,带入,把枚举倍数的放在一边,枚举约数的放在另一边,倍数从1开始枚举,就变形成了:,接下来就非常显然了,O(sqrt(n))就可以搞定了.只不过要在线性筛的时候预处理一下μ函数的前缀和,方法就是对于每一个质数,计算它对它的倍数的贡献,复杂度差不多是O(nlogn)级别的.
一个非常常用的技巧:将求倍数的放在一边,将求约数的放在另一边,倍数从1开始枚举,很多题都会用到这个变形.
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll maxn = 1e7+5; ll prime[1000010], tot, vis[10000010], mo[10000010], sum[10000010], T, ans, n, m; void init() { mo[1] = 1; for (ll i = 2; i <= maxn; i++) { if (!vis[i]) { prime[++tot] = i; mo[i] = -1; } for (ll j = 1; j <= tot; j++) { ll t = i * prime[j]; if (t > maxn) break; vis[t] = 1; if (i % prime[j] == 0) { mo[t] = 0; break; } mo[t] = -mo[i]; } } for (ll i = 1; i <= tot; i++) { ll x = prime[i]; for (ll j = x; j <= maxn; j += x) sum[j] += mo[j/x]; } for (ll i = 1; i <= maxn; i++) sum[i] += sum[i - 1]; } int main() { init(); scanf("%lld", &T); while (T--) { ans = 0; ll last = 0; scanf("%lld%lld", &n, &m); for (ll i = 1; i <= min(n, m); i = last + 1) { last = min(n / (n / i), m / (m / i)); ans += (sum[last] - sum[i - 1]) * (n / i) * (m / i); } printf("%lld ", ans); } return 0; }