【NOI2016】循环之美

题面

题解

前面的题目转化可以看这篇 blog,这里直接给出式子:

[sum_{i = 1}^n sum_{j = 1}^m [i perp j][j perp k] ]

其中 ([n perp m] = [gcd(n, m) = 1])

方法一:化简 ([i perp j])

[egin{aligned} &sum_{i = 1}^n sum_{j = 1}^m [i perp j][j perp k]\ =&sum_{i = 1}^n sum_{j = 1}^m [j perp k] sum_{d | i, d | j} mu(d)\ =&sum_{d = 1}^n mu(d) sum_{d | i} sum_{d | j} [j perp k]\ =&sum_{d = 1}^n [d perp k] mu(d) leftlfloor frac nd ight floor sum_{j = 1}^{m/d} [j perp k]\ end{aligned} ]

(f(n) = sum_{i = 1}^n [i perp k], g(n, k) = sum_{i=1}^n mu(i)[i perp k])

首先可以得出:

[f(n) = leftlfloor frac nk ight floor f(k) + f(n mod k) ]

证明可以见 M_sea 的 blog

接下来要求出 (g(n, k))。当 (k eq 1) 时:

[egin{aligned} g(n, k) &= sum_{i = 1}^n mu(i)[i perp k]\ &= sum_{i=1}^n mu(i) sum_{d|i, d|k} mu(d)\ &= sum_{d|k} mu(d)sum_{d|i} mu(i)\ &= sum_{d|k} mu(d)sum_{i=1}^{n/d} mu(id)\ &= sum_{d|k} mu^2(d)sum_{i=1}^{n/d} mu(i)[i perp d]\ &= sum_{d|k} mu^2(d) gleft(leftlfloor frac nd ight floor, d ight) end{aligned} ]

其中 (mu^2(x) = mu(x)^2)

否则 (g(n, 1))(mu) 的前缀和,杜教筛即可。

方法二:化简 ([j perp k])

(f(n, m, k) = sum_{i=1}^nsum_{j=1}^m[i perp j][j perp k]),那么:

[egin{aligned} f(n,m,k) &= sum_{i=1}^nsum_{j=1}^m [iperp j] sum_{d|j,d|k} mu(d)\ &= sum_{d|k}mu(d)sum_{d|j}sum_{i=1}^n[i perp j]\ &= sum_{d|k}mu(d)sum_{j=1}^{m/d}sum_{i=1}^n[iperp j][i perp d]\ &= sum_{d|k}mu(d)fleft(leftlfloor frac md ight floor, n, d ight) end{aligned} ]

边界为 (f(0, m, k) = f(n, 0, k) = 0) 以及 (f(n, m, 1) = sum_{i=1}^nsum_{j=1}^m[iperp j] = sum_{d=1}^n mu(d) lfloor frac nd floor lfloor frac md floor)

同样可以杜教筛 (mu) 解决问题。

代码

方法一

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<map>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define clear(x, y) memset(x, y, sizeof(x))

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while(ch != '-' && (!isdigit(ch))) ch = getchar();
	if(ch == '-') w = -1, ch = getchar();
	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int N(10000000), maxn(N + 10), maxk(5010);
int n, m, K, f[maxk], not_prime[maxn];
int prime[maxn], mu[maxn], smu[maxn], cnt;
std::map<std::pair<int, int>, int> sum;

void Init()
{
	for(RG int i = 1; i <= K; i++) f[i] = f[i - 1] + (std::__gcd(i, K) == 1);
	not_prime[1] = mu[1] = 1;
	for(RG int i = 2; i <= N; i++)
	{
		if(!not_prime[i]) prime[++cnt] = i, mu[i] = -1;
		for(RG int j = 1; j <= cnt && 1ll * i * prime[j] <= N; j++)
		{
			not_prime[i * prime[j]] = 1;
			if(i % prime[j]) mu[i * prime[j]] = -mu[i];
			else break;
		}
	}
	for(RG int i = 1; i <= N; i++) smu[i] = smu[i - 1] + mu[i];
}

int Sf(int x) { return (x / K) * f[K] + f[x % K]; }
int Sum(int x, int k)
{
	if(x == 0) return 0;
	if(k == 1 && x <= N) return smu[x];
	if(sum.find(std::make_pair(x, k)) != sum.end())
		return sum[std::make_pair(x, k)];
	int &res = sum[std::make_pair(x, k)]; res = 0;
	if(k == 1)
	{
		res = 1; for(RG int i = 2, j; i <= x; i = j + 1)
			j = x / (x / i), res -= (j - i + 1) * Sum(x / i, 1);
	}
	else
	{
		for(RG int i = 1; i * i <= k; i++) if(k % i == 0)
		{
			if(mu[i]) res += Sum(x / i, i);
			if(i * i != k && mu[k / i]) res += Sum(x / (k / i), k / i);
		}
	}
	return res;
}

int main()
{
#ifndef ONLINE_JUDGE
	file(cpp);
#endif
	n = read(), m = read(), K = read();
	Init(); long long ans = 0;
	for(RG int i = 1, j, min = std::min(n, m); i <= min; i = j + 1)
		j = std::min(n / (n / i), m / (m / i)), ans += 1ll *
			(Sum(j, K) - Sum(i - 1, K)) * (n / i) * Sf(m / i);
	printf("%lld
", ans);
	return 0;
}

方法二

#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)

const int N(2010), M(1e6 + 10), LIM(1e6);
std::map<std::pair<std::pair<int, int>, int>, long long> f;
int n, m, K, mu[M], smu[M];
std::vector<int> fac[N]; std::map<int, long long> S;

long long Smu(int x)
{
	if (x <= M) return smu[x];
	if (S.find(x) != S.end()) return S[x];
	long long &y = S[x]; y = 1;
	for (int i = 2, j; i <= x; i = j + 1)
		j = x / (x / i), y -= Smu(x / i) * (j - i + 1);
	return y;
}

long long F(int n, int m, int K)
{
	if (n == 0 || m == 0) return 0;
	auto P = std::make_pair(std::make_pair(n, m), K);
	if (f.find(P) != f.end()) return f[P];
	long long &y = f[P]; y = 0;
	if (K == 1)
	{
		if (n > m) std::swap(n, m);
		for (int i = 1, j; i <= n; i = j + 1)
			j = std::min(n / (n / i), m / (m / i)), y += (Smu(j) - Smu(i - 1)) * (n / i) * (m / i);
	}
	else for (auto i : fac[K]) y += F(m / i, n, i) * mu[i];
	return y;
}

int main()
{
#ifndef ONLINE_JUDGE
	file(cpp);
#endif
	scanf("%d%d%d", &n, &m, &K), mu[1] = 1;
	for (int i = 1; (i << 1) <= LIM; i++)
		for (int j = (i << 1); j <= LIM; j += i) mu[j] -= mu[i];
	for (int i = 1; i <= K; i++)
		for (int j = i; j <= K; j += i) if (mu[i]) fac[j].push_back(i);
	for (int i = 1; i <= LIM; i++) smu[i] = smu[i - 1] + mu[i];
	printf("%lld
", F(n, m, K));
	return 0;
}
原文地址:https://www.cnblogs.com/cj-xxz/p/13080919.html