bzoj2694 Lcm
2694: Lcm
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 438 Solved: 230
[Submit][Status][Discuss]
Description
对于任意的>1的n gcd(a, b)不是n^2的倍数
也就是说gcd(a, b)没有一个因子的次数>=2
也就是说gcd(a, b)没有一个因子的次数>=2
Input
一个正整数T表示数据组数
接下来T行 每行两个正整数 表示N、M
Output
T行 每行一个整数 表示第i组数据的结果
Sample Input
4
2 4
3 3
6 5
8 3
2 4
3 3
6 5
8 3
Sample Output
24
28
233
178
28
233
178
HINT
T <= 10000
N, M<=4000000
分析:和bzoj2693是差不多的.但是这道题特别容易错.
根据bzoj2693推出来的公式:,这道题因为n,m比较小,可以直接枚举倍数来预处理后面那一坨式子. 因为有gcd(a,b)没有平方因子的限制,所以只更新μ(i) != 0的倍数.
直接按照上述式子预处理是错的. 因为最后一步进行了一个转化:因数是成对出现的,将D / i变成了i. 如果在μ函数没有限制的情况下,这种转化是对的,现在有了限制,就会将需要统计进入答案的μ没有统计进入答案.
为什么会发生这种情况呢?,令D = di. 先知道了d,然后确定D,才能知道i. 所以i在这道题中必须用D / d来表示.
因为是对2^30取模,中途利用int的自然溢出即可. 注意:中间运算中会有除法,如果能除一定要除,避免爆int.
#include <cstdio>
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 4000010,maxx = 4000000,mod = 1 << 30; int T,n,m,mu[maxn + 10],prime[maxn + 10],vis[maxn + 10],tot,sum[maxn + 10],ans; int Sum(int x,int y) { return (x * (x + 1) / 2) * (y * (y + 1) / 2); } void solve(int x,int y) { ans = 0; int last = 0; if (x > y) swap(x,y); for (int i = 1; i <= x; i = last + 1) { last = min(x / (x / i), y / (y / i)); ans += Sum(x / i,y / i) * (sum[last] - sum[i - 1]); } } void init() { mu[1] = 1; for (int i = 2; i <= maxx; i++) { if (!vis[i]) { prime[++tot] = i; mu[i] = -1; } for (int j = 1; j <= tot; j++) { int t = prime[j] * i; if (t > maxx) break; vis[t] = 1; if (i % prime[j] == 0) { mu[t] = 0; break; } mu[t] = -mu[i]; } } for (int i = 1; i <= maxx; i++) if (mu[i] != 0) { for (int j = i; j <= maxx; j += i)
sum[j] += mu[j / i] * j / i * j; //先除! } for (int i = 1; i <= maxx; i++) sum[i] += sum[i - 1]; } int main() { init(); scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); solve(n,m); printf("%d\n",(ans % mod + mod) % mod); //为了避免出现负数 } return 0; }