BZOJ 3309: DZY Loves Math (莫比乌斯反演)

反演比较简单,可以化成Ans=k=1min(n,m)nkmkdkμ(kd)f(d)large Ans=sum_{k=1}^{min(n,m)}lfloor{frac{n}{k}} floorlfloor{frac{m}{k}} floorsum_{d|k}mu(frac kd)f(d)

后面这一坨怎么推的具体见 FromATP的博客

不看题解我不会aaa…w(゚Д゚)w

CODE

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 10000000;
int prime[MAXN/10], cnt, e[MAXN+5], g[MAXN+5];
LL F[MAXN+5];
bool vis[MAXN+5];
inline void Pre_Work(int n) {
	F[1] = e[1] = 0; g[1] = 1;
	for(int i = 2; i <= n; ++i) {
		if(!vis[i])
			prime[++cnt] = i, F[i] = e[i] = 1, g[i] = i;
		for(int j = 1; j <= cnt && i*prime[j] <= n; ++j) {
			vis[i*prime[j]] = 1;
			if(i % prime[j] == 0) {
				g[i*prime[j]] = g[i] * prime[j];
				e[i*prime[j]] = e[i] + 1;
				int tmp = i / g[i];
				if(tmp == 1) F[i*prime[j]] = 1;
				else F[i*prime[j]] = (e[i*prime[j]] == e[tmp] ? -F[tmp] : 0);
				break;
			}
			g[i*prime[j]] = prime[j];
			e[i*prime[j]] = 1;
			F[i*prime[j]] = (e[i] == 1 ? -F[i] : 0);
		}
	}
	for(int i = 2; i <= n; ++i) F[i] += F[i-1];
}

inline LL solve(int n, int m) {
	if(m < n) swap(n, m);
	LL re = 0;
	for(int i = 1, j; i <= n; i = j+1) {
		j = min(n/(n/i), m/(m/i));
		re += 1ll * (n/i) * (m/i) * (F[j] - F[i-1]);
	}
	return re;
}
int main() {
	int n, m, T;
	scanf("%d", &T);
	Pre_Work(MAXN);
	while(T--) {
		scanf("%d%d", &n, &m);
		printf("%lld
", solve(n, m));
	}
}
原文地址:https://www.cnblogs.com/Orz-IE/p/12039291.html