数论题单

我发现kuangbin题单里的数论好像有点水,都是些线筛之类的

杜教筛

洛谷模板(杜教筛)

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

template<typename T>void read(T& x) {
	x = 0;
	int p = 1;
	char c = getchar();	
	while (!isdigit(c)) { if (c == '-')p = -1, c = getchar(); }
	while (isdigit(c)) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	x *= p;
}

template<typename T>void write(T x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

typedef long long ll;
const int maxn = 6e6 + 10;
int mu[maxn];
ll phi[maxn];
int sum_mu[maxn];
ll sum_phi[maxn];
int vis[maxn];
int prime[maxn], cnt;
unordered_map<int, ll>MU, PHI;

void get(int N) {
	phi[1] = mu[1] = 1;
	for (int i = 2; i <= N; i++) {
		if (!vis[i]) {
			prime[++cnt] = i;
			mu[i] = -1;
			phi[i] = i - 1;
		}
		for (int j = 1; j <= cnt && prime[j] * i <= N; j++) {
			vis[prime[j] * i] = 1;
			if (i % prime[j] == 0) {
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}
			else {
				mu[i * prime[j]] = mu[i] * -1;
				phi[i * prime[j]] = phi[i] * (prime[j] - 1);
			}
		}
	}
	for (int i = 1; i <= N; i++) {
		sum_phi[i] = sum_phi[i - 1] + phi[i];
		sum_mu[i] = sum_mu[i - 1] + mu[i];
	}
}
ll djs_mu(ll x) {
	if (x <= maxn - 10)return sum_mu[x];
	if (MU.count(x) != 0)return MU[x];
	int ans = 1;
	for (int l = 2, r;l <= x; l = r + 1) {
		r = x / (x / l);
		ans -= (r - l + 1) * djs_mu(x / l);
	}
	return MU[x] = ans;
}

ll djs_phi(ll x) {
	if (x <= maxn - 10)return sum_phi[x];
	if (PHI.count(x) != 0)return PHI[x];
	ll ans = x * (x + 1) / 2;
	for (ll l = 2, r; l <= x; l = r + 1) {
		r = x / (x / l);
		ans -= (r - l + 1) * djs_phi(x / l);
	}
	return PHI[x] = ans;
}

int main() {
	int t, n;
	read(t);
	get(maxn - 10);
	while (t--) {
		read(n);
		write(djs_phi(n)); putchar(' ');
		write(djs_mu(n)); puts("");
	}
}

莫比乌斯反演

约数和

YY的GCD

P3455 [POI2007]ZAP-Queries

[sum_{i=1}^nsum_{j=1}^m[gcd(i,j)=1] ]

[ o sum_{i=1}^nsum_{j=1}^msum_{d|gcd(i,j)}mu(d) ]

[ o sum_{d=1}^nmu(d)*lfloor dfrac nd floor * lfloor dfrac md floor ]

分块搞就完事了

for (int l = 1, r; l <= min(n, m); l = r + 1) {
			r = min(n / (n / l), m / (m / l));
			ans += (n / l) * (m / l) * (sum[r] - sum[l - 1]);
}

[sum_{i=1}^nsum_{j=1}^m[gcd(i,j)=k] ]

[sum_{i=1}^{lfloor frac nk floor}sum_{j=1}^{lfloor frac mk floor}[gcd(i,j) = 1] ]

题目链接

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;
int vis[maxn], prime[maxn], cnt, mu[maxn], sum[maxn];

void get_mu() {
	mu[1] = 1;
	for (int i = 2; i < maxn; i++) {
		if (!vis[i]) {
			prime[++cnt] = i;
			mu[i] = -1;
		}
		for (int j = 1; j <= cnt && prime[j] * i < maxn; j++) {
			vis[prime[j] * i] = 1;
			if (i % prime[j] == 0) break;
			mu[prime[j] * i] = mu[i] * -1;
		}
	}
	for (int i = 1; i < maxn; i++) sum[i] = sum[i - 1] + mu[i];
}

int T, n, m, k;
int main() {
	scanf("%d", &T);
	get_mu();

	while (T--) {
		scanf("%d%d%d", &n, &m, &k);
		n /= k; m /= k;
		long long ans = 0;
		for (int l = 1, r; l <= min(n, m); l = r + 1) {
			r = min(n / (n / l), m / (m / l));
			//这里没有加long long转换wa了一次
			ans += 1ll* (n / l) * (m / l) * (sum[r] - sum[l - 1]);
		}
		printf("%lld
", ans);
	}
}
原文地址:https://www.cnblogs.com/sduwh/p/13708433.html